@elaraai/east-ui 1.0.80 → 1.0.82
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/dist/src/charts/chart/index.d.ts +4 -0
- package/dist/src/charts/chart/index.d.ts.map +1 -1
- package/dist/src/charts/spec/index.d.ts +8 -0
- package/dist/src/charts/spec/index.d.ts.map +1 -1
- package/dist/src/collections/deck/index.d.ts +8 -0
- package/dist/src/collections/deck/index.d.ts.map +1 -1
- package/dist/src/collections/index.d.ts +2 -2
- package/dist/src/collections/index.d.ts.map +1 -1
- package/dist/src/collections/index.js.map +1 -1
- package/dist/src/collections/library/index.d.ts +4 -0
- package/dist/src/collections/library/index.d.ts.map +1 -1
- package/dist/src/collections/library/types.d.ts +4 -0
- package/dist/src/collections/library/types.d.ts.map +1 -1
- package/dist/src/collections/matrix/index.d.ts +56 -19
- package/dist/src/collections/matrix/index.d.ts.map +1 -1
- package/dist/src/collections/matrix/index.js +38 -24
- package/dist/src/collections/matrix/index.js.map +1 -1
- package/dist/src/collections/plan/assemble.d.ts +655 -2113
- package/dist/src/collections/plan/assemble.d.ts.map +1 -1
- package/dist/src/collections/plan/assemble.js +159 -136
- package/dist/src/collections/plan/assemble.js.map +1 -1
- package/dist/src/collections/plan/builders.d.ts +193 -40
- package/dist/src/collections/plan/builders.d.ts.map +1 -1
- package/dist/src/collections/plan/builders.js +162 -32
- package/dist/src/collections/plan/builders.js.map +1 -1
- package/dist/src/collections/plan/factories.d.ts +290 -116
- package/dist/src/collections/plan/factories.d.ts.map +1 -1
- package/dist/src/collections/plan/factories.js +281 -156
- package/dist/src/collections/plan/factories.js.map +1 -1
- package/dist/src/collections/plan/index.d.ts +111 -58
- package/dist/src/collections/plan/index.d.ts.map +1 -1
- package/dist/src/collections/plan/index.js +62 -31
- package/dist/src/collections/plan/index.js.map +1 -1
- package/dist/src/collections/plan/ir.d.ts +759 -1769
- package/dist/src/collections/plan/ir.d.ts.map +1 -1
- package/dist/src/collections/plan/ir.js +51 -27
- package/dist/src/collections/plan/ir.js.map +1 -1
- package/dist/src/collections/plan/pick.d.ts +89 -71
- package/dist/src/collections/plan/pick.d.ts.map +1 -1
- package/dist/src/collections/plan/pick.js +101 -86
- package/dist/src/collections/plan/pick.js.map +1 -1
- package/dist/src/collections/plan/root.d.ts +164 -77
- package/dist/src/collections/plan/root.d.ts.map +1 -1
- package/dist/src/collections/plan/root.js +350 -42
- package/dist/src/collections/plan/root.js.map +1 -1
- package/dist/src/collections/plan/series.d.ts +12524 -10527
- package/dist/src/collections/plan/series.d.ts.map +1 -1
- package/dist/src/collections/plan/series.js +1596 -298
- package/dist/src/collections/plan/series.js.map +1 -1
- package/dist/src/collections/plan/types.d.ts +6115 -2802
- package/dist/src/collections/plan/types.d.ts.map +1 -1
- package/dist/src/collections/plan/types.js +575 -170
- package/dist/src/collections/plan/types.js.map +1 -1
- package/dist/src/collections/sheet/bridge.d.ts +154 -43
- package/dist/src/collections/sheet/bridge.d.ts.map +1 -1
- package/dist/src/collections/sheet/bridge.js +383 -240
- package/dist/src/collections/sheet/bridge.js.map +1 -1
- package/dist/src/collections/sheet/columns.d.ts +66 -16
- package/dist/src/collections/sheet/columns.d.ts.map +1 -1
- package/dist/src/collections/sheet/columns.js +25 -25
- package/dist/src/collections/sheet/columns.js.map +1 -1
- package/dist/src/collections/sheet/context-bridge.d.ts +48 -0
- package/dist/src/collections/sheet/context-bridge.d.ts.map +1 -0
- package/dist/src/collections/sheet/context-bridge.js +216 -0
- package/dist/src/collections/sheet/context-bridge.js.map +1 -0
- package/dist/src/collections/sheet/draft-bridge.d.ts +37 -0
- package/dist/src/collections/sheet/draft-bridge.d.ts.map +1 -0
- package/dist/src/collections/sheet/draft-bridge.js +153 -0
- package/dist/src/collections/sheet/draft-bridge.js.map +1 -0
- package/dist/src/collections/sheet/drafts.d.ts +14 -0
- package/dist/src/collections/sheet/drafts.d.ts.map +1 -0
- package/dist/src/collections/sheet/drafts.js +14 -0
- package/dist/src/collections/sheet/drafts.js.map +1 -0
- package/dist/src/collections/sheet/editing-bridge.d.ts +36 -0
- package/dist/src/collections/sheet/editing-bridge.d.ts.map +1 -0
- package/dist/src/collections/sheet/editing-bridge.js +196 -0
- package/dist/src/collections/sheet/editing-bridge.js.map +1 -0
- package/dist/src/collections/sheet/editing-types.d.ts +268 -0
- package/dist/src/collections/sheet/editing-types.d.ts.map +1 -0
- package/dist/src/collections/sheet/editing-types.js +34 -0
- package/dist/src/collections/sheet/editing-types.js.map +1 -0
- package/dist/src/collections/sheet/edits.d.ts +37 -0
- package/dist/src/collections/sheet/edits.d.ts.map +1 -0
- package/dist/src/collections/sheet/edits.js +31 -0
- package/dist/src/collections/sheet/edits.js.map +1 -0
- package/dist/src/collections/sheet/group.d.ts +165 -0
- package/dist/src/collections/sheet/group.d.ts.map +1 -0
- package/dist/src/collections/sheet/group.js +52 -0
- package/dist/src/collections/sheet/group.js.map +1 -0
- package/dist/src/collections/sheet/index.d.ts +171 -27
- package/dist/src/collections/sheet/index.d.ts.map +1 -1
- package/dist/src/collections/sheet/index.js +99 -17
- package/dist/src/collections/sheet/index.js.map +1 -1
- package/dist/src/collections/sheet/link.d.ts +22 -16
- package/dist/src/collections/sheet/link.d.ts.map +1 -1
- package/dist/src/collections/sheet/link.js +18 -12
- package/dist/src/collections/sheet/link.js.map +1 -1
- package/dist/src/collections/sheet/registers.d.ts +152 -5
- package/dist/src/collections/sheet/registers.d.ts.map +1 -1
- package/dist/src/collections/sheet/registers.js +154 -5
- package/dist/src/collections/sheet/registers.js.map +1 -1
- package/dist/src/collections/sheet/root.d.ts +175 -26
- package/dist/src/collections/sheet/root.d.ts.map +1 -1
- package/dist/src/collections/sheet/root.js +150 -26
- package/dist/src/collections/sheet/root.js.map +1 -1
- package/dist/src/collections/sheet/seed-bridge.d.ts +14 -0
- package/dist/src/collections/sheet/seed-bridge.d.ts.map +1 -0
- package/dist/src/collections/sheet/seed-bridge.js +51 -0
- package/dist/src/collections/sheet/seed-bridge.js.map +1 -0
- package/dist/src/collections/sheet/sub-rows.d.ts +249 -0
- package/dist/src/collections/sheet/sub-rows.d.ts.map +1 -0
- package/dist/src/collections/sheet/sub-rows.js +222 -0
- package/dist/src/collections/sheet/sub-rows.js.map +1 -0
- package/dist/src/collections/sheet/transactions.d.ts +76 -0
- package/dist/src/collections/sheet/transactions.d.ts.map +1 -0
- package/dist/src/collections/sheet/transactions.js +40 -0
- package/dist/src/collections/sheet/transactions.js.map +1 -0
- package/dist/src/collections/sheet/types.d.ts +12034 -3651
- package/dist/src/collections/sheet/types.d.ts.map +1 -1
- package/dist/src/collections/sheet/types.js +365 -48
- package/dist/src/collections/sheet/types.js.map +1 -1
- package/dist/src/collections/table/index.d.ts +98 -123
- package/dist/src/collections/table/index.d.ts.map +1 -1
- package/dist/src/collections/table/index.js +97 -111
- package/dist/src/collections/table/index.js.map +1 -1
- package/dist/src/collections/table/types.d.ts +87 -31
- package/dist/src/collections/table/types.d.ts.map +1 -1
- package/dist/src/collections/table/types.js +51 -26
- package/dist/src/collections/table/types.js.map +1 -1
- package/dist/src/collections/value-tree/flatten.d.ts.map +1 -1
- package/dist/src/collections/value-tree/flatten.js +4 -1
- package/dist/src/collections/value-tree/flatten.js.map +1 -1
- package/dist/src/component.d.ts +5075 -4079
- package/dist/src/component.d.ts.map +1 -1
- package/dist/src/component.js +46 -46
- package/dist/src/component.js.map +1 -1
- package/dist/src/contracts/approval.d.ts +10 -8
- package/dist/src/contracts/approval.d.ts.map +1 -1
- package/dist/src/contracts/approval.js +10 -8
- package/dist/src/contracts/approval.js.map +1 -1
- package/dist/src/contracts/drag.d.ts +11 -9
- package/dist/src/contracts/drag.d.ts.map +1 -1
- package/dist/src/contracts/drag.js +28 -21
- package/dist/src/contracts/drag.js.map +1 -1
- package/dist/src/contracts/editing.d.ts +1283 -0
- package/dist/src/contracts/editing.d.ts.map +1 -0
- package/dist/src/contracts/editing.js +711 -0
- package/dist/src/contracts/editing.js.map +1 -0
- package/dist/src/contracts/index.d.ts +2 -1
- package/dist/src/contracts/index.d.ts.map +1 -1
- package/dist/src/contracts/index.js +2 -1
- package/dist/src/contracts/index.js.map +1 -1
- package/dist/src/contracts/pick.d.ts +6 -7
- package/dist/src/contracts/pick.d.ts.map +1 -1
- package/dist/src/contracts/pick.js +6 -7
- package/dist/src/contracts/pick.js.map +1 -1
- package/dist/src/contracts/review.d.ts +3 -1
- package/dist/src/contracts/review.d.ts.map +1 -1
- package/dist/src/contracts/review.js +3 -1
- package/dist/src/contracts/review.js.map +1 -1
- package/dist/src/contracts/source.d.ts +270 -80
- package/dist/src/contracts/source.d.ts.map +1 -1
- package/dist/src/contracts/source.js +357 -58
- package/dist/src/contracts/source.js.map +1 -1
- package/dist/src/display/chip-rail/index.d.ts.map +1 -1
- package/dist/src/display/chip-rail/index.js +10 -10
- package/dist/src/display/chip-rail/index.js.map +1 -1
- package/dist/src/extension.d.ts +2 -1
- package/dist/src/extension.d.ts.map +1 -1
- package/dist/src/extension.js +2 -1
- package/dist/src/extension.js.map +1 -1
- package/dist/src/format/index.d.ts +1 -154
- package/dist/src/format/index.d.ts.map +1 -1
- package/dist/src/format/types.d.ts +35 -23
- package/dist/src/format/types.d.ts.map +1 -1
- package/dist/src/format/types.js +16 -14
- package/dist/src/format/types.js.map +1 -1
- package/dist/src/forms/field/index.js +4 -4
- package/dist/src/forms/field/index.js.map +1 -1
- package/dist/src/forms/file-upload/index.js +16 -16
- package/dist/src/forms/file-upload/index.js.map +1 -1
- package/dist/src/index.d.ts +4 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +8 -3
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +11 -3
- package/dist/src/internal.js.map +1 -1
- package/dist/src/layout/box/index.d.ts.map +1 -1
- package/dist/src/layout/box/index.js +47 -47
- package/dist/src/layout/box/index.js.map +1 -1
- package/dist/src/layout/flex/index.d.ts.map +1 -1
- package/dist/src/layout/flex/index.js +47 -47
- package/dist/src/layout/flex/index.js.map +1 -1
- package/dist/src/layout/grid/index.js +11 -11
- package/dist/src/layout/grid/index.js.map +1 -1
- package/dist/src/layout/scroll-area/index.d.ts.map +1 -1
- package/dist/src/layout/scroll-area/index.js +8 -8
- package/dist/src/layout/scroll-area/index.js.map +1 -1
- package/dist/src/layout/separator/index.d.ts.map +1 -1
- package/dist/src/layout/separator/index.js +6 -6
- package/dist/src/layout/separator/index.js.map +1 -1
- package/dist/src/layout/stack/index.d.ts.map +1 -1
- package/dist/src/layout/stack/index.js +46 -46
- package/dist/src/layout/stack/index.js.map +1 -1
- package/dist/src/layout/sticky/index.d.ts.map +1 -1
- package/dist/src/layout/sticky/index.js +8 -8
- package/dist/src/layout/sticky/index.js.map +1 -1
- package/dist/src/overlays/action-bar/index.d.ts.map +1 -1
- package/dist/src/overlays/action-bar/index.js +8 -8
- package/dist/src/overlays/action-bar/index.js.map +1 -1
- package/dist/src/platform/index.d.ts +1 -1
- package/dist/src/platform/index.d.ts.map +1 -1
- package/dist/src/platform/index.js +1 -1
- package/dist/src/platform/index.js.map +1 -1
- package/dist/src/platform/slice/impl.d.ts +62 -51
- package/dist/src/platform/slice/impl.d.ts.map +1 -1
- package/dist/src/platform/slice/impl.js +155 -72
- package/dist/src/platform/slice/impl.js.map +1 -1
- package/dist/src/platform/slice/index.d.ts +3744 -2226
- package/dist/src/platform/slice/index.d.ts.map +1 -1
- package/dist/src/platform/slice/index.js +43 -1
- package/dist/src/platform/slice/index.js.map +1 -1
- package/dist/src/runtime/collections/deck.d.ts +4 -0
- package/dist/src/runtime/collections/deck.d.ts.map +1 -1
- package/dist/src/runtime/collections/matrix.d.ts +10 -9
- package/dist/src/runtime/collections/matrix.d.ts.map +1 -1
- package/dist/src/runtime/collections/matrix.js +11 -6
- package/dist/src/runtime/collections/matrix.js.map +1 -1
- package/dist/src/runtime/collections/plan.d.ts +148 -65
- package/dist/src/runtime/collections/plan.d.ts.map +1 -1
- package/dist/src/runtime/collections/plan.js +155 -67
- package/dist/src/runtime/collections/plan.js.map +1 -1
- package/dist/src/runtime/collections/sheet.d.ts +56 -23
- package/dist/src/runtime/collections/sheet.d.ts.map +1 -1
- package/dist/src/runtime/collections/sheet.js +30 -22
- package/dist/src/runtime/collections/sheet.js.map +1 -1
- package/dist/src/runtime/collections/table.d.ts +18 -11
- package/dist/src/runtime/collections/table.d.ts.map +1 -1
- package/dist/src/runtime/collections/table.js +7 -4
- package/dist/src/runtime/collections/table.js.map +1 -1
- package/dist/src/shared/reify.d.ts +4 -46
- package/dist/src/shared/reify.d.ts.map +1 -1
- package/dist/src/shared/reify.js +4 -61
- package/dist/src/shared/reify.js.map +1 -1
- package/dist/src/shared/tree.d.ts +88 -0
- package/dist/src/shared/tree.d.ts.map +1 -0
- package/dist/src/shared/tree.js +134 -0
- package/dist/src/shared/tree.js.map +1 -0
- package/dist/src/slice/breakdown/index.d.ts +4 -0
- package/dist/src/slice/breakdown/index.d.ts.map +1 -1
- package/dist/src/slice/breakdown/types.d.ts +4 -0
- package/dist/src/slice/breakdown/types.d.ts.map +1 -1
- package/dist/src/slice/cohort/index.d.ts +24 -4
- package/dist/src/slice/cohort/index.d.ts.map +1 -1
- package/dist/src/slice/cohort/index.js +8 -4
- package/dist/src/slice/cohort/index.js.map +1 -1
- package/dist/src/slice/cohort/types.d.ts +11 -0
- package/dist/src/slice/cohort/types.d.ts.map +1 -1
- package/dist/src/slice/cohort/types.js +7 -0
- package/dist/src/slice/cohort/types.js.map +1 -1
- package/dist/src/slice/filter/index.d.ts +4 -0
- package/dist/src/slice/filter/index.d.ts.map +1 -1
- package/dist/src/slice/filter/types.d.ts +4 -0
- package/dist/src/slice/filter/types.d.ts.map +1 -1
- package/dist/src/slice/index.d.ts +736 -0
- package/dist/src/slice/index.d.ts.map +1 -1
- package/dist/src/slice/legend/index.d.ts +4 -0
- package/dist/src/slice/legend/index.d.ts.map +1 -1
- package/dist/src/slice/legend/types.d.ts +4 -0
- package/dist/src/slice/legend/types.d.ts.map +1 -1
- package/dist/src/slice/rail/index.d.ts +8 -4
- package/dist/src/slice/rail/index.d.ts.map +1 -1
- package/dist/src/slice/rail/index.js.map +1 -1
- package/dist/src/slice/range/index.d.ts +4 -0
- package/dist/src/slice/range/index.d.ts.map +1 -1
- package/dist/src/slice/range/types.d.ts +4 -0
- package/dist/src/slice/range/types.d.ts.map +1 -1
- package/dist/src/slice/search/index.d.ts +4 -0
- package/dist/src/slice/search/index.d.ts.map +1 -1
- package/dist/src/slice/search/types.d.ts +4 -0
- package/dist/src/slice/search/types.d.ts.map +1 -1
- package/dist/src/slice/summary/index.d.ts +4 -0
- package/dist/src/slice/summary/index.d.ts.map +1 -1
- package/dist/src/slice/summary/types.d.ts +4 -0
- package/dist/src/slice/summary/types.d.ts.map +1 -1
- package/dist/src/typography/code/index.js +1 -1
- package/dist/src/typography/code/index.js.map +1 -1
- package/dist/src/typography/code-block/index.js +7 -7
- package/dist/src/typography/code-block/index.js.map +1 -1
- package/dist/src/typography/heading/index.js +2 -2
- package/dist/src/typography/heading/index.js.map +1 -1
- package/dist/src/typography/list/index.js +1 -1
- package/dist/src/typography/list/index.js.map +1 -1
- package/dist/src/typography/mark/index.js +1 -1
- package/dist/src/typography/mark/index.js.map +1 -1
- package/dist/src/typography/note/index.js +1 -1
- package/dist/src/typography/note/index.js.map +1 -1
- package/dist/src/typography/numeric/index.d.ts +3 -308
- package/dist/src/typography/numeric/index.d.ts.map +1 -1
- package/dist/src/typography/numeric/index.js +4 -4
- package/dist/src/typography/numeric/index.js.map +1 -1
- package/dist/src/typography/numeric/types.d.ts +1 -154
- package/dist/src/typography/numeric/types.d.ts.map +1 -1
- package/dist/src/typography/text/index.js +1 -1
- package/dist/src/typography/text/index.js.map +1 -1
- package/dist/test/collections/matrix.examples.d.ts +7 -3
- package/dist/test/collections/matrix.examples.d.ts.map +1 -1
- package/dist/test/collections/matrix.examples.js +55 -24
- package/dist/test/collections/matrix.examples.js.map +1 -1
- package/dist/test/collections/paged-source.examples.d.ts +4 -0
- package/dist/test/collections/paged-source.examples.d.ts.map +1 -1
- package/dist/test/collections/paged-source.examples.js +98 -13
- package/dist/test/collections/paged-source.examples.js.map +1 -1
- package/dist/test/collections/plan.examples.d.ts +101 -26
- package/dist/test/collections/plan.examples.d.ts.map +1 -1
- package/dist/test/collections/plan.examples.js +1481 -755
- package/dist/test/collections/plan.examples.js.map +1 -1
- package/dist/test/collections/sheet-link.examples.d.ts +7 -0
- package/dist/test/collections/sheet-link.examples.d.ts.map +1 -0
- package/dist/test/collections/sheet-link.examples.js +41 -0
- package/dist/test/collections/sheet-link.examples.js.map +1 -0
- package/dist/test/collections/sheet-transactions.examples.d.ts +7 -0
- package/dist/test/collections/sheet-transactions.examples.d.ts.map +1 -0
- package/dist/test/collections/sheet-transactions.examples.js +58 -0
- package/dist/test/collections/sheet-transactions.examples.js.map +1 -0
- package/dist/test/collections/sheet.examples.d.ts +52 -5
- package/dist/test/collections/sheet.examples.d.ts.map +1 -1
- package/dist/test/collections/sheet.examples.js +500 -158
- package/dist/test/collections/sheet.examples.js.map +1 -1
- package/dist/test/collections/table.examples.d.ts +31 -3
- package/dist/test/collections/table.examples.d.ts.map +1 -1
- package/dist/test/collections/table.examples.js +188 -28
- package/dist/test/collections/table.examples.js.map +1 -1
- package/dist/test/contracts/editing.examples.d.ts +7 -0
- package/dist/test/contracts/editing.examples.d.ts.map +1 -0
- package/dist/test/contracts/editing.examples.js +58 -0
- package/dist/test/contracts/editing.examples.js.map +1 -0
- package/dist/test/layout/splitter.examples.d.ts.map +1 -1
- package/dist/test/layout/splitter.examples.js +3 -1
- package/dist/test/layout/splitter.examples.js.map +1 -1
- package/dist/test/platform/slice.examples.d.ts.map +1 -1
- package/dist/test/platform/slice.examples.js +1 -0
- package/dist/test/platform/slice.examples.js.map +1 -1
- package/dist/test/slice/slice.examples.d.ts.map +1 -1
- package/dist/test/slice/slice.examples.js +5 -2
- package/dist/test/slice/slice.examples.js.map +1 -1
- package/package.json +4 -3
- package/dist/src/collections/plan/data-forms.d.ts +0 -332
- package/dist/src/collections/plan/data-forms.d.ts.map +0 -1
- package/dist/src/collections/plan/data-forms.js +0 -212
- package/dist/src/collections/plan/data-forms.js.map +0 -1
|
@@ -4,13 +4,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "@elaraai/east-ui/jsx-runtime";
|
|
|
4
4
|
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
5
5
|
*/
|
|
6
6
|
/** @jsxImportSource @elaraai/east-ui */
|
|
7
|
-
import { East, ArrayType, BooleanType, DateTimeType, DictType, FloatType, IntegerType, NullType, OptionType, StringType, StructType, example, none, some, variant, } from "@elaraai/east";
|
|
7
|
+
import { East, ArrayType, BooleanType, DateTimeType, DictType, FloatType, IntegerType, NullType, OptionType, StringType, StructType, VariantType, example, none, some, variant, } from "@elaraai/east";
|
|
8
8
|
import { Badge, Box, Configurator, Format, HStack, Input, Paged, Reactive, SegmentGroup, Sheet, Slice, State, Status, Style, Switch, Text, UIComponentType, VStack, } from "@elaraai/east-ui";
|
|
9
9
|
// ============================================================================
|
|
10
10
|
// The Sheet corpus — the five slots of EXAMPLES_AUTHORING.md §8 (Sheet Spec.md §8 P1):
|
|
11
11
|
// `sheetBasic` · `sheetVariants` (THE configurator) · `sheetPlan` (the flagship)
|
|
12
12
|
// · the behavioural isolates `sheetCopilot` / `sheetLens` / `sheetWriteBack` /
|
|
13
|
-
// `
|
|
13
|
+
// `sheetGrouped` / `sheetPaged` / `sheetReadiness` / `sheetInsertion` /
|
|
14
|
+
// `sheetSubRows` / `sheetRules` / `sheetRegisters` / `sheetLoose` · `sheetStress`. Every example is self-contained — types,
|
|
14
15
|
// fixtures and constructors inside the body, bulk data derived East-side —
|
|
15
16
|
// and the fixtures are the prototype's synthetic registers and rows: a
|
|
16
17
|
// discrete manufacturing plant (machines on lines, work orders moving parts
|
|
@@ -29,7 +30,7 @@ const ACTIVITY_TYPE = StructType({
|
|
|
29
30
|
name: StringType, uom: StringType, rate: FloatType, fte: IntegerType, days: IntegerType, sides: Sheet.Types.Sides,
|
|
30
31
|
});
|
|
31
32
|
/** The model behind an async proposer — an e3 function, a service, a notebook; the Sheet only needs the types. */
|
|
32
|
-
const planRecommend = East.asyncPlatform("sheet_plan_recommend", [Sheet.Types.
|
|
33
|
+
const planRecommend = East.asyncPlatform("sheet_plan_recommend", [Sheet.Types.DraftContext(PLAN_ROW_TYPE, ACTIVITY_TYPE)], ArrayType(Sheet.Types.Proposal(PLAN_ROW_TYPE)), { optional: true });
|
|
33
34
|
/**
|
|
34
35
|
* The smallest sheet (§3.1) — three typed columns over the host's structs,
|
|
35
36
|
* `onUpdate` receiving the whole collection with the edit applied (the
|
|
@@ -37,7 +38,7 @@ const planRecommend = East.asyncPlatform("sheet_plan_recommend", [Sheet.Types.Co
|
|
|
37
38
|
*/
|
|
38
39
|
export const sheetBasic = example({
|
|
39
40
|
keywords: ["Sheet", "Root", "basic", "column", "date", "text", "quantity", "onUpdate", "State", "Reactive", "id"],
|
|
40
|
-
description: "The smallest sheet — three typed columns over the host's structs, the whole collection written
|
|
41
|
+
description: "The smallest sheet — three typed columns over the host's structs, draft changes reviewed together and the whole collection written once on Apply",
|
|
41
42
|
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
42
43
|
const JobType = StructType({
|
|
43
44
|
id: StringType,
|
|
@@ -48,8 +49,8 @@ export const sheetBasic = example({
|
|
|
48
49
|
const jobs = $.let(State.bind([ArrayType(JobType)], "sheet_basic_jobs", [
|
|
49
50
|
{ id: "j1", start: none, task: "Machining", qty: none },
|
|
50
51
|
]));
|
|
51
|
-
return (_jsx(Sheet, { data: jobs
|
|
52
|
-
start: Sheet.column.date(JobType, { header: "Start", sub: "
|
|
52
|
+
return (_jsx(Sheet, { data: jobs, id: "id", columns: {
|
|
53
|
+
start: Sheet.column.date(JobType, { header: "Start", sub: "dd / mm / yyyy" }),
|
|
53
54
|
task: Sheet.column.text(JobType, { header: "Task" }),
|
|
54
55
|
qty: Sheet.column.quantity(JobType, { header: "Qty" }), // no driver on this sheet — the two-argument form
|
|
55
56
|
}, onUpdate: jobs.write }));
|
|
@@ -64,11 +65,11 @@ export const sheetBasic = example({
|
|
|
64
65
|
* log to the reactive aside.
|
|
65
66
|
*/
|
|
66
67
|
export const sheetVariants = example({
|
|
67
|
-
keywords: ["Sheet", "Root", "configurator", "density", "blanks", "readOnly", "height", "fill", "scroll", "#320", "copilot", "fill", "provider", "onSelect", "
|
|
68
|
+
keywords: ["Sheet", "Root", "configurator", "density", "blanks", "readOnly", "height", "fill", "scroll", "#320", "copilot", "fill", "provider", "onSelect", "onPatch", "Configurator", "SegmentGroup", "Switch", "Input", "Reactive", "State"],
|
|
68
69
|
description: "Sheet configurator — density, the blank tail, read-only, size mode (auto / scroll / fill) and the copilot switch all expression-fed into one live sheet; selection and edits log to the aside",
|
|
69
70
|
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
70
71
|
const JobType = StructType({ id: StringType, start: OptionType(DateTimeType), task: StringType, qty: OptionType(FloatType), notes: StringType });
|
|
71
|
-
const Ctx = Sheet.Types.
|
|
72
|
+
const Ctx = Sheet.Types.DraftContext(JobType);
|
|
72
73
|
const rows = $.let(State.bind([ArrayType(JobType)], "sheet_variants_rows", [
|
|
73
74
|
{ id: "j1", start: some(new Date("2026-02-16T00:00:00Z")), task: "Machining", qty: some(1200.0), notes: "Rough the P-40 blanks" },
|
|
74
75
|
{ id: "j2", start: some(new Date("2026-03-09T00:00:00Z")), task: "Painting", qty: some(250.0), notes: "" },
|
|
@@ -100,17 +101,14 @@ export const sheetVariants = example({
|
|
|
100
101
|
const NoteFill = OptionType(Sheet.Types.Fill(StringType));
|
|
101
102
|
const phrase = $.const(East.function([Ctx], NoteFill, ($, ctx) => {
|
|
102
103
|
const noFill = $.const(none, NoteFill);
|
|
103
|
-
return copilotBind.read().and(() =>
|
|
104
|
+
return ctx.row.task.match({ value: (_$, task) => copilotBind.read().and(() => task.length().greater(0n)).ifElse(() => some({ value: East.str `${task} as planned`, meta: "phrasing from the supplied task" }), () => noFill),
|
|
105
|
+
}, () => noFill);
|
|
104
106
|
}));
|
|
105
107
|
const onSelect = $.const(East.function([Sheet.Types.Selection], NullType, ($, sel) => {
|
|
106
108
|
$(lastEventBind.write(East.str `onSelect: row ${sel.rowId.match({ some: (_$, id) => id, none: (_$) => "—" })} · ${sel.key.match({ some: (_$, k) => k, none: (_$) => "—" })}`));
|
|
107
109
|
}));
|
|
108
|
-
const
|
|
109
|
-
$(lastEventBind.write(e.
|
|
110
|
-
commit: (_$, c) => East.str `commit: ${c.rowId} · ${c.key} · ${c.source.getTag()}`,
|
|
111
|
-
insert: (_$, i) => East.str `insert: ${i.row.id}`,
|
|
112
|
-
remove: (_$, r) => East.str `remove: ${r.rowIds.length()} rows`,
|
|
113
|
-
})));
|
|
110
|
+
const onPatch = $.const(East.function([Sheet.Types.PatchEvent(JobType)], NullType, ($, e) => {
|
|
111
|
+
$(lastEventBind.write(East.str `${e.origin.getTag()} · ${e.draftChanges.length()} entries · ${e.readiness.getTag()}`));
|
|
114
112
|
}));
|
|
115
113
|
const densitySel = $.let(densities.filter((_$, v) => v.getTag().equal(dKey)).get(0n));
|
|
116
114
|
// Size mode — an empty height reads as unbounded, so ONE sheet
|
|
@@ -122,12 +120,12 @@ export const sheetVariants = example({
|
|
|
122
120
|
Configurator.Control("Size", sKey, _jsx(SegmentGroup, { value: sKey, onChange: onSize, size: "sm", items: sizeModes.map((_$, m) => SegmentGroup.Item(m, _jsx(Text, { children: m.upperCase() }))) })),
|
|
123
121
|
Configurator.Control("Blank tail", East.print(blanks), _jsx(Input.Integer, { value: blanks, min: 0n, max: 40n, step: 2n, size: "sm", onChange: onBlanks })),
|
|
124
122
|
Configurator.Slot("Chrome", _jsxs(HStack, { gap: "5", align: "center", wrap: "wrap", children: [_jsx(Switch, { checked: readOnly, label: "Read-only", onChange: onReadOnly }), _jsx(Switch, { checked: copilotOn, label: "Copilot", onChange: onCopilot })] })),
|
|
125
|
-
], preview: _jsx(Box, { width: "100%", height: boxHeight, overflow: "hidden", children: _jsx(Sheet, { data: rows
|
|
126
|
-
start: Sheet.column.date(JobType, { header: "Start", sub: "
|
|
123
|
+
], preview: _jsx(Box, { width: "100%", height: boxHeight, overflow: "hidden", children: _jsx(Sheet, { data: rows, id: "id", columns: {
|
|
124
|
+
start: Sheet.column.date(JobType, { header: "Start", sub: "dd / mm / yyyy", width: "96px" }),
|
|
127
125
|
task: Sheet.column.text(JobType, { header: "Task", width: "180px" }),
|
|
128
|
-
qty: Sheet.column.quantity(JobType, { header: "Qty", sub: "1,200 ·
|
|
126
|
+
qty: Sheet.column.quantity(JobType, { header: "Qty", sub: "1,200 · pcs", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }) }),
|
|
129
127
|
notes: Sheet.column.text(JobType, { header: "Notes", sub: "free text", fill: [phrase] }),
|
|
130
|
-
}, density: densitySel, blanks: blanks, readOnly: readOnly, onSelect: onSelect,
|
|
128
|
+
}, density: densitySel, blanks: blanks, readOnly: readOnly, onSelect: onSelect, onPatch: onPatch, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { notes: "" })), onUpdate: rows.write, style: { height: sheetHeight } }) }), aside: {
|
|
131
129
|
label: "Events · Reactive",
|
|
132
130
|
body: (_jsx(Badge, { colorPalette: "brand", variant: "outline", children: East.equal(lastEvent.length(), 0n).ifElse((_$) => "Interact with the sheet", (_$) => lastEvent) })),
|
|
133
131
|
}, spec: [
|
|
@@ -156,7 +154,7 @@ export const sheetPlan = example({
|
|
|
156
154
|
const LineType = StructType({ code: StringType, name: StringType, machines: IntegerType, aliases: ArrayType(StringType) });
|
|
157
155
|
const FamilyType = StructType({ name: StringType, aliases: ArrayType(StringType) });
|
|
158
156
|
const StatusType = StructType({ word: StringType, tone: Status.Types.Value });
|
|
159
|
-
const Ctx = Sheet.Types.
|
|
157
|
+
const Ctx = Sheet.Types.DraftContext(PlanRowType, ActivityType);
|
|
160
158
|
const Proposals = ArrayType(Sheet.Types.Proposal(PlanRowType));
|
|
161
159
|
// The synthetic registers — a discrete manufacturing plant: machines on
|
|
162
160
|
// lines, work orders moving parts between them.
|
|
@@ -247,7 +245,10 @@ export const sheetPlan = example({
|
|
|
247
245
|
// The arity rule (§3.4) — how many machines the To half should hold, from the quantity.
|
|
248
246
|
const impliedStations = $.const(East.function([Ctx], OptionType(Sheet.Types.Counted), ($, ctx) => {
|
|
249
247
|
const noCount = $.const(none, OptionType(Sheet.Types.Counted));
|
|
250
|
-
|
|
248
|
+
$.if(ctx.row.qty.hasTag("value").not(), ($) => { $.return(noCount); });
|
|
249
|
+
const supplied = $.const(ctx.row.qty.unwrap("value"));
|
|
250
|
+
$.if(supplied.hasTag("some").not(), ($) => { $.return(noCount); });
|
|
251
|
+
const qty = $.const(supplied.unwrap("some"));
|
|
251
252
|
const piecesPerMachine = $.const(300.0);
|
|
252
253
|
// ⌈qty ÷ 300⌉ and round(qty) by hand — `toInteger` refuses a fraction.
|
|
253
254
|
const share = $.let(qty.divide(piecesPerMachine));
|
|
@@ -266,12 +267,12 @@ export const sheetPlan = example({
|
|
|
266
267
|
const siteMatches = $.const(East.function([CheckCtx], OptionType(StringType), ($, c) => {
|
|
267
268
|
const noFlag = $.const(none, OptionType(StringType));
|
|
268
269
|
const site = $.let(c.half.match({ from: (_$) => c.row.fromSite, to: (_$) => c.row.toSite }));
|
|
269
|
-
return c.member.match({
|
|
270
|
-
identified: (_$, m) => site.equal("").not()
|
|
270
|
+
return site.hasTag("value").ifElse(() => c.member.match({
|
|
271
|
+
identified: (_$, m) => site.unwrap("value").equal("").not()
|
|
271
272
|
.and(() => machineSites.has(m.key))
|
|
272
|
-
.and(() => machineSites.get(m.key).equal(site).not())
|
|
273
|
-
.ifElse((_$2) => East.value(some(East.str `${m.key} is not at ${site}`), OptionType(StringType)), (_$2) => noFlag),
|
|
274
|
-
}, (_$) => noFlag);
|
|
273
|
+
.and(() => machineSites.get(m.key).equal(site.unwrap("value")).not())
|
|
274
|
+
.ifElse((_$2) => East.value(some(East.str `${m.key} is not at ${site.unwrap("value")}`), OptionType(StringType)), (_$2) => noFlag),
|
|
275
|
+
}, (_$) => noFlag), () => noFlag);
|
|
275
276
|
}));
|
|
276
277
|
// The fills (§3.5) — derive, history, sequence, default, phrase, capacity — as author functions.
|
|
277
278
|
const DateFill = OptionType(Sheet.Types.Fill(DateTimeType));
|
|
@@ -281,38 +282,52 @@ export const sheetPlan = example({
|
|
|
281
282
|
const endFromStart = $.const(East.function([Ctx], DateFill, ($, ctx) => {
|
|
282
283
|
const noFill = $.const(none, DateFill);
|
|
283
284
|
return ctx.row.start.match({
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
285
|
+
value: (_$, supplied) => supplied.match({
|
|
286
|
+
none: () => noFill,
|
|
287
|
+
some: (_$, start) => ctx.driver.match({
|
|
288
|
+
none: () => noFill,
|
|
289
|
+
some: (_$, d) => some({ value: start.addDays(d.days), meta: East.str `+${d.days}d · ${d.name}` }),
|
|
290
|
+
}),
|
|
288
291
|
}),
|
|
289
|
-
});
|
|
292
|
+
}, () => noFill);
|
|
290
293
|
}));
|
|
291
|
-
const lastSimilar = $.const(East.function([Ctx], OptionType(PlanRowType), ($, ctx) => {
|
|
292
|
-
const
|
|
293
|
-
return
|
|
294
|
+
const lastSimilar = $.const(East.function([Ctx], OptionType(Sheet.Types.Draft(PlanRowType)), ($, ctx) => {
|
|
295
|
+
const noRow = $.const(none, OptionType(Sheet.Types.Draft(PlanRowType)));
|
|
296
|
+
return ctx.row.activity.match({
|
|
297
|
+
value: ($, activity) => {
|
|
298
|
+
const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.hasTag("value").and(() => r.activity.unwrap("value").equal(activity))));
|
|
299
|
+
return similar.size().equal(0n).ifElse(() => noRow, () => some(similar.get(similar.size().subtract(1n))));
|
|
300
|
+
},
|
|
301
|
+
}, () => noRow);
|
|
294
302
|
}));
|
|
295
303
|
const lastQuantity = $.const(East.function([Ctx], FloatFill, ($, ctx) => {
|
|
296
304
|
const noFill = $.const(none, FloatFill);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
305
|
+
return ctx.row.activity.match({
|
|
306
|
+
value: ($, activity) => {
|
|
307
|
+
const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.hasTag("value").and(() => r.activity.unwrap("value").equal(activity))
|
|
308
|
+
.and(() => r.qty.hasTag("value")).and(() => r.qty.unwrap("value").hasTag("some"))));
|
|
309
|
+
return similar.size().equal(0n).ifElse(() => noFill, ($) => {
|
|
310
|
+
const row = $.let(similar.get(similar.size().subtract(1n)));
|
|
311
|
+
return some({ value: row.qty.unwrap("value").unwrap("some"), meta: "last supplied quantity for this activity" });
|
|
312
|
+
});
|
|
313
|
+
},
|
|
314
|
+
}, () => noFill);
|
|
302
315
|
}));
|
|
303
316
|
const lastStations = $.const(East.function([Ctx], LinkFill, ($, ctx) => {
|
|
304
317
|
const noFill = $.const(none, LinkFill);
|
|
305
318
|
const similar = $.const(lastSimilar);
|
|
306
319
|
return similar(ctx).match({
|
|
307
|
-
none: (
|
|
308
|
-
some: (_$, r) => r.stations.
|
|
320
|
+
none: () => noFill,
|
|
321
|
+
some: (_$, r) => r.stations.match({
|
|
322
|
+
value: (_$, stations) => stations.from.size().add(stations.to.size()).equal(0n).ifElse(() => noFill, () => some({ value: stations, meta: "same stations as the last similar row" })),
|
|
323
|
+
}, () => noFill),
|
|
309
324
|
});
|
|
310
325
|
}));
|
|
311
326
|
const nextSlot = $.const(East.function([Ctx], DateFill, ($, ctx) => {
|
|
312
|
-
const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("some")));
|
|
327
|
+
const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("value").and(() => r.start.unwrap("value").hasTag("some"))));
|
|
313
328
|
return dated.length().greater(0n).ifElse(($2) => {
|
|
314
329
|
const last = $2.let(dated.get(dated.length().subtract(1n)));
|
|
315
|
-
return East.value(some({ value: last.start.unwrap("some").addDays(7n), meta: East.str `week after ${last.id}` }), DateFill);
|
|
330
|
+
return East.value(some({ value: last.start.unwrap("value").unwrap("some").addDays(7n), meta: East.str `week after ${last.id.match({ value: (_$, id) => id }, () => "previous row")}` }), DateFill);
|
|
316
331
|
}, ($2) => {
|
|
317
332
|
const daysToMonday = $2.let(East.value(8n, IntegerType).subtract(ctx.today.getDayOfWeek()).remainder(7n));
|
|
318
333
|
const monday = $2.let(ctx.today.addDays(daysToMonday.equal(0n).ifElse((_$) => 7n, (_$) => daysToMonday)));
|
|
@@ -328,11 +343,12 @@ export const sheetPlan = example({
|
|
|
328
343
|
}));
|
|
329
344
|
const phrase = $.const(East.function([Ctx], TextFill, ($, ctx) => {
|
|
330
345
|
const noFill = $.const(none, TextFill);
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
346
|
+
return ctx.row.activity.hasTag("value").and(() => ctx.row.qty.hasTag("value"))
|
|
347
|
+
.and(() => ctx.row.qty.unwrap("value").hasTag("some")).ifElse(($) => {
|
|
348
|
+
const activity = $.const(ctx.row.activity.unwrap("value"));
|
|
349
|
+
const qty = $.const(ctx.row.qty.unwrap("value").unwrap("some"));
|
|
350
|
+
return activity.startsWith("Machining").ifElse(() => some({ value: East.str `Machine ${qty} P-40 blanks`, meta: "phrasing from supplied activity and quantity" }), () => activity.startsWith("Painting").ifElse(() => some({ value: East.str `Paint ${qty} P-40 housings`, meta: "phrasing from supplied activity and quantity" }), () => noFill));
|
|
351
|
+
}, () => noFill);
|
|
336
352
|
}));
|
|
337
353
|
const countedByQuantity = $.const(East.function([Ctx], LinkFill, ($, ctx) => {
|
|
338
354
|
const noFill = $.const(none, LinkFill);
|
|
@@ -346,39 +362,52 @@ export const sheetPlan = example({
|
|
|
346
362
|
// The proposers (§3.6) — a domain pattern, a learned follower, and an ASYNC model call.
|
|
347
363
|
const roughingFollowUps = $.const(East.function([Ctx], Proposals, ($, ctx) => {
|
|
348
364
|
const empty = $.const([], Proposals);
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
{ patch: Sheet.patch(PlanRowType, { activity: "Machining", start: some(
|
|
357
|
-
meta: "finishing · end +3…+7 d" },
|
|
365
|
+
return ctx.row.activity.hasTag("value")
|
|
366
|
+
.and(() => ctx.row.activity.unwrap("value").equal("Machining - Roughing"))
|
|
367
|
+
.and(() => ctx.row.end.hasTag("value"))
|
|
368
|
+
.and(() => ctx.row.end.unwrap("value").hasTag("some")).ifElse(($) => {
|
|
369
|
+
const end = $.const(ctx.row.end.unwrap("value").unwrap("some"));
|
|
370
|
+
return $.const([
|
|
371
|
+
{ patch: Sheet.patch(PlanRowType, { activity: "Inspection", start: some(end), end: some(end), qty: some(4.0), notes: "Inspect 4 lots" }), meta: "inspection at the supplied end" },
|
|
372
|
+
{ patch: Sheet.patch(PlanRowType, { activity: "Machining", start: some(end.addDays(3n)), end: some(end.addDays(7n)), notes: "Finish the roughed blanks" }), meta: "finishing · end +3…+7 d" },
|
|
358
373
|
], Proposals);
|
|
359
|
-
}, (
|
|
374
|
+
}, () => empty);
|
|
360
375
|
}));
|
|
361
376
|
const lastFollower = $.const(East.function([Ctx], Proposals, ($, ctx) => {
|
|
362
377
|
const empty = $.const([], Proposals);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
378
|
+
return ctx.row.activity.hasTag("value")
|
|
379
|
+
.and(() => ctx.row.start.hasTag("value"))
|
|
380
|
+
.and(() => ctx.row.start.unwrap("value").hasTag("some")).ifElse(($) => {
|
|
381
|
+
const activity = $.const(ctx.row.activity.unwrap("value"));
|
|
382
|
+
const start = $.const(ctx.row.start.unwrap("value").unwrap("some"));
|
|
383
|
+
const dated = $.const(ctx.rows.filter((_$, r) => r.start.hasTag("value")
|
|
384
|
+
.and(() => r.start.unwrap("value").hasTag("some"))
|
|
385
|
+
.and(() => r.activity.hasTag("value"))));
|
|
386
|
+
const upper = $.const(dated.size().greater(1n).ifElse(() => dated.size().subtract(1n), () => 0n));
|
|
387
|
+
const pairs = $.const(East.Array.range(0n, upper).filter((_$, i) => dated.get(i).activity.unwrap("value").equal(activity)
|
|
388
|
+
.and(() => dated.get(i.add(1n)).activity.unwrap("value").equal(activity).not())));
|
|
389
|
+
return pairs.size().equal(0n).ifElse(() => empty, ($) => {
|
|
390
|
+
const index = $.const(pairs.get(pairs.size().subtract(1n)));
|
|
391
|
+
const from = $.const(dated.get(index));
|
|
392
|
+
const next = $.const(dated.get(index.add(1n)));
|
|
393
|
+
const gap = $.const(next.start.unwrap("value").unwrap("some").toEpochMilliseconds()
|
|
394
|
+
.subtract(from.start.unwrap("value").unwrap("some").toEpochMilliseconds()));
|
|
395
|
+
return $.const([{ patch: Sheet.patch(PlanRowType, { activity: next.activity.unwrap("value"), start: some(start.addMilliseconds(gap)) }),
|
|
396
|
+
meta: "follower learned from supplied activities and dates" }], Proposals);
|
|
397
|
+
});
|
|
398
|
+
}, () => empty);
|
|
399
|
+
}));
|
|
400
|
+
const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, ($, ctx) => {
|
|
401
|
+
const result = $.let([], Proposals);
|
|
402
|
+
$.try(($) => { $.assign(result, planRecommend(ctx)); }).catch(($, message) => {
|
|
403
|
+
// The standalone showcase has no model backend. Other failures
|
|
404
|
+
// still reach the Sheet's provider diagnostic.
|
|
405
|
+
$.if(message.notEqual("Platform function 'sheet_plan_recommend' is not available"), ($) => { $.error(message); });
|
|
377
406
|
});
|
|
407
|
+
return result;
|
|
378
408
|
}));
|
|
379
|
-
const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, (_$, ctx) => planRecommend(ctx)));
|
|
380
409
|
const planned = $.let(rows.filter((_$, r) => r.activity.length().greater(0n)).length());
|
|
381
|
-
return (_jsx(Sheet, { data:
|
|
410
|
+
return (_jsx(Sheet, { data: plan, id: "id", owned: r => r.orderCode.length().greater(0n).or(() => r.status.equal("CANCELLED")), driver: Sheet.driver("activity", activities, { key: a => a.name, label: a => a.name }), registers: {
|
|
382
411
|
stations: Sheet.register.concat([
|
|
383
412
|
Sheet.register.members(machines, { kind: "machine", key: m => m.code, label: m => m.code,
|
|
384
413
|
meta: m => some(m.family), parent: m => some(m.line) }),
|
|
@@ -391,7 +420,7 @@ export const sheetPlan = example({
|
|
|
391
420
|
sites: Sheet.register.members(sites, { kind: "site", key: s => s, label: s => s }),
|
|
392
421
|
statuses: Sheet.register.members(statuses, { kind: "status", key: s => s.word, label: s => s.word, tone: s => some(s.tone) }),
|
|
393
422
|
}, columns: {
|
|
394
|
-
start: Sheet.column.date(PlanRowType, { header: "Start", sub: "
|
|
423
|
+
start: Sheet.column.date(PlanRowType, { header: "Start", sub: "dd / mm / yyyy", width: "96px", fill: [nextSlot] }),
|
|
395
424
|
end: Sheet.column.date(PlanRowType, { header: "End", sub: "4d = start+4", width: "96px", base: "start", fill: [endFromStart] }),
|
|
396
425
|
activity: Sheet.column.lookup(PlanRowType, { header: "Activity", sub: "activity register", width: "214px" }),
|
|
397
426
|
qty: Sheet.column.quantity(PlanRowType, ActivityType, {
|
|
@@ -416,7 +445,9 @@ export const sheetPlan = example({
|
|
|
416
445
|
orderCode: Sheet.column.stamped(PlanRowType, { header: "Order code", sub: "stamped on release", owner: "ERP", width: "104px" }),
|
|
417
446
|
status: Sheet.column.enum(PlanRowType, "statuses", { header: "Status", sub: "erp", width: "124px" }),
|
|
418
447
|
}, suggest: { ahead: 2n, triggers: ["activity", "start", "end", "qty", "notes", "stations"],
|
|
419
|
-
propose: [roughingFollowUps, modelProposals, lastFollower] }, slice: slice, affordances: ["search", "filter"], views: views.read(), onViewsChange: views.write,
|
|
448
|
+
propose: [roughingFollowUps, modelProposals, lastFollower] }, slice: slice, affordances: ["search", "filter"], views: views.read(), onViewsChange: views.write, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(PlanRowType), () => Sheet.patch(PlanRowType, {
|
|
449
|
+
notes: "", stations: { from: [], to: [] }, fromSite: "", toSite: "", orderCode: "", status: "PLANNED",
|
|
450
|
+
})), onUpdate: plan.write, footer: [{ text: East.str `${planned} planned` }], style: { height: "fill" } }));
|
|
420
451
|
} }))),
|
|
421
452
|
inputs: [],
|
|
422
453
|
});
|
|
@@ -426,10 +457,10 @@ export const sheetPlan = example({
|
|
|
426
457
|
* similar row's quantity), sequence (a week after the last dated row), default
|
|
427
458
|
* (eight hours at the driver's rate), a domain pattern proposer, a learned
|
|
428
459
|
* follower, and an ASYNC proposer behind a platform function; every take
|
|
429
|
-
* logs its provenance through `
|
|
460
|
+
* logs its provenance through `onPatch`.
|
|
430
461
|
*/
|
|
431
462
|
export const sheetCopilot = example({
|
|
432
|
-
keywords: ["Sheet", "Root", "copilot", "fill", "provider", "propose", "proposer", "suggest", "derive", "history", "sequence", "default", "learned", "follower", "asyncFunction", "asyncPlatform", "pending", "latest wins", "provenance", "
|
|
463
|
+
keywords: ["Sheet", "Root", "copilot", "fill", "provider", "propose", "proposer", "suggest", "derive", "history", "sequence", "default", "learned", "follower", "asyncFunction", "asyncPlatform", "pending", "latest wins", "provenance", "onPatch", "source", "Context", "Fill", "Patch", "Proposal", "Reactive", "State"],
|
|
433
464
|
description: "The copilot in isolation — derive, history, sequence and default fills, a pattern proposer, a learned follower and an async model proposer, all author East functions over the typed context; takes log their provenance",
|
|
434
465
|
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
435
466
|
const PlanRowType = StructType({
|
|
@@ -438,7 +469,7 @@ export const sheetCopilot = example({
|
|
|
438
469
|
fromSite: StringType, toSite: StringType, orderCode: StringType, status: StringType,
|
|
439
470
|
});
|
|
440
471
|
const ActivityType = StructType({ name: StringType, uom: StringType, rate: FloatType, fte: IntegerType, days: IntegerType, sides: Sheet.Types.Sides });
|
|
441
|
-
const Ctx = Sheet.Types.
|
|
472
|
+
const Ctx = Sheet.Types.DraftContext(PlanRowType, ActivityType);
|
|
442
473
|
const Proposals = ArrayType(Sheet.Types.Proposal(PlanRowType));
|
|
443
474
|
const activities = $.const([
|
|
444
475
|
{ name: "Machining", uom: "pcs", rate: 60.0, fte: 2n, days: 4n, sides: variant("both", null) },
|
|
@@ -459,28 +490,35 @@ export const sheetCopilot = example({
|
|
|
459
490
|
const endFromStart = $.const(East.function([Ctx], DateFill, ($, ctx) => {
|
|
460
491
|
const noFill = $.const(none, DateFill);
|
|
461
492
|
return ctx.row.start.match({
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
493
|
+
value: (_$, supplied) => supplied.match({
|
|
494
|
+
none: () => noFill,
|
|
495
|
+
some: (_$, start) => ctx.driver.match({
|
|
496
|
+
none: () => noFill,
|
|
497
|
+
some: (_$, d) => some({ value: start.addDays(d.days), meta: East.str `+${d.days}d · ${d.name}` }),
|
|
498
|
+
}),
|
|
466
499
|
}),
|
|
467
|
-
});
|
|
500
|
+
}, () => noFill);
|
|
468
501
|
}));
|
|
469
502
|
// history — the last row above with this activity.
|
|
470
503
|
const lastQuantity = $.const(East.function([Ctx], FloatFill, ($, ctx) => {
|
|
471
504
|
const noFill = $.const(none, FloatFill);
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
505
|
+
return ctx.row.activity.match({
|
|
506
|
+
value: ($, activity) => {
|
|
507
|
+
const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.activity.hasTag("value").and(() => r.activity.unwrap("value").equal(activity))
|
|
508
|
+
.and(() => r.qty.hasTag("value")).and(() => r.qty.unwrap("value").hasTag("some"))));
|
|
509
|
+
return similar.size().equal(0n).ifElse(() => noFill, ($) => {
|
|
510
|
+
const row = $.let(similar.get(similar.size().subtract(1n)));
|
|
511
|
+
return some({ value: row.qty.unwrap("value").unwrap("some"), meta: "last supplied quantity for this activity" });
|
|
512
|
+
});
|
|
513
|
+
},
|
|
514
|
+
}, () => noFill);
|
|
477
515
|
}));
|
|
478
516
|
// sequence — a week after the nearest dated row above; else next Monday.
|
|
479
517
|
const nextSlot = $.const(East.function([Ctx], DateFill, ($, ctx) => {
|
|
480
|
-
const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("some")));
|
|
518
|
+
const dated = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.start.hasTag("value").and(() => r.start.unwrap("value").hasTag("some"))));
|
|
481
519
|
return dated.length().greater(0n).ifElse(($2) => {
|
|
482
520
|
const last = $2.let(dated.get(dated.length().subtract(1n)));
|
|
483
|
-
return East.value(some({ value: last.start.unwrap("some").addDays(7n), meta: East.str `week after ${last.id}` }), DateFill);
|
|
521
|
+
return East.value(some({ value: last.start.unwrap("value").unwrap("some").addDays(7n), meta: East.str `week after ${last.id.match({ value: (_$, id) => id }, () => "previous row")}` }), DateFill);
|
|
484
522
|
}, ($2) => {
|
|
485
523
|
const daysToMonday = $2.let(East.value(8n, IntegerType).subtract(ctx.today.getDayOfWeek()).remainder(7n));
|
|
486
524
|
const monday = $2.let(ctx.today.addDays(daysToMonday.equal(0n).ifElse((_$) => 7n, (_$) => daysToMonday)));
|
|
@@ -498,54 +536,66 @@ export const sheetCopilot = example({
|
|
|
498
536
|
// A domain pattern — a roughing run is followed by an inspection and a finishing run.
|
|
499
537
|
const roughingFollowUps = $.const(East.function([Ctx], Proposals, ($, ctx) => {
|
|
500
538
|
const empty = $.const([], Proposals);
|
|
501
|
-
return ctx.row.activity.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
539
|
+
return ctx.row.activity.hasTag("value")
|
|
540
|
+
.and(() => ctx.row.activity.unwrap("value").equal("Machining - Roughing"))
|
|
541
|
+
.and(() => ctx.row.end.hasTag("value"))
|
|
542
|
+
.and(() => ctx.row.end.unwrap("value").hasTag("some")).ifElse(($) => {
|
|
543
|
+
const end = $.const(ctx.row.end.unwrap("value").unwrap("some"));
|
|
544
|
+
return $.const([
|
|
545
|
+
{ patch: Sheet.patch(PlanRowType, { activity: "Inspection", start: some(end), end: some(end), qty: some(4.0), notes: "Inspect 4 lots" }), meta: "inspection at the supplied end" },
|
|
546
|
+
{ patch: Sheet.patch(PlanRowType, { activity: "Machining", start: some(end.addDays(3n)), end: some(end.addDays(7n)), notes: "Finish the roughed blanks" }), meta: "finishing · end +3…+7 d" },
|
|
508
547
|
], Proposals);
|
|
509
|
-
}, (
|
|
548
|
+
}, () => empty);
|
|
510
549
|
}));
|
|
511
550
|
// Learned from the sheet — what followed this activity last time, after how long.
|
|
512
551
|
const lastFollower = $.const(East.function([Ctx], Proposals, ($, ctx) => {
|
|
513
552
|
const empty = $.const([], Proposals);
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
553
|
+
return ctx.row.activity.hasTag("value")
|
|
554
|
+
.and(() => ctx.row.start.hasTag("value"))
|
|
555
|
+
.and(() => ctx.row.start.unwrap("value").hasTag("some")).ifElse(($) => {
|
|
556
|
+
const activity = $.const(ctx.row.activity.unwrap("value"));
|
|
557
|
+
const start = $.const(ctx.row.start.unwrap("value").unwrap("some"));
|
|
558
|
+
const dated = $.const(ctx.rows.filter((_$, r) => r.start.hasTag("value")
|
|
559
|
+
.and(() => r.start.unwrap("value").hasTag("some"))
|
|
560
|
+
.and(() => r.activity.hasTag("value"))));
|
|
561
|
+
const upper = $.const(dated.size().greater(1n).ifElse(() => dated.size().subtract(1n), () => 0n));
|
|
562
|
+
const pairs = $.const(East.Array.range(0n, upper).filter((_$, i) => dated.get(i).activity.unwrap("value").equal(activity)
|
|
563
|
+
.and(() => dated.get(i.add(1n)).activity.unwrap("value").equal(activity).not())));
|
|
564
|
+
return pairs.size().equal(0n).ifElse(() => empty, ($) => {
|
|
565
|
+
const index = $.const(pairs.get(pairs.size().subtract(1n)));
|
|
566
|
+
const from = $.const(dated.get(index));
|
|
567
|
+
const next = $.const(dated.get(index.add(1n)));
|
|
568
|
+
const gap = $.const(next.start.unwrap("value").unwrap("some").toEpochMilliseconds()
|
|
569
|
+
.subtract(from.start.unwrap("value").unwrap("some").toEpochMilliseconds()));
|
|
570
|
+
return $.const([{ patch: Sheet.patch(PlanRowType, { activity: next.activity.unwrap("value"), start: some(start.addMilliseconds(gap)) }),
|
|
571
|
+
meta: "follower learned from supplied activities and dates" }], Proposals);
|
|
572
|
+
});
|
|
573
|
+
}, () => empty);
|
|
529
574
|
}));
|
|
530
575
|
// A model — an ASYNC proposer; the strip shows a pending chip and a newer context cancels the wait.
|
|
531
|
-
const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, (
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
576
|
+
const modelProposals = $.const(East.asyncFunction([Ctx], Proposals, ($, ctx) => {
|
|
577
|
+
const result = $.let([], Proposals);
|
|
578
|
+
$.try(($) => { $.assign(result, planRecommend(ctx)); }).catch(($, message) => {
|
|
579
|
+
// The standalone showcase has no model backend. Other failures
|
|
580
|
+
// still reach the Sheet's provider diagnostic.
|
|
581
|
+
$.if(message.notEqual("Platform function 'sheet_plan_recommend' is not available"), ($) => { $.error(message); });
|
|
582
|
+
});
|
|
583
|
+
return result;
|
|
584
|
+
}));
|
|
585
|
+
// Every gesture logs once, including incomplete drafts and undo.
|
|
586
|
+
const onPatch = $.const(East.function([Sheet.Types.PatchEvent(PlanRowType)], NullType, ($, e) => {
|
|
587
|
+
$(log.write(log.read().concat([East.str `${e.origin.getTag()} · ${e.label} · ${e.draftChanges.length()} entries`])));
|
|
540
588
|
}));
|
|
541
589
|
const entries = $.let(log.read());
|
|
542
|
-
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: plan
|
|
590
|
+
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: plan, id: "id", driver: Sheet.driver("activity", activities, { key: a => a.name, label: a => a.name }), columns: {
|
|
543
591
|
start: Sheet.column.date(PlanRowType, { header: "Start", sub: "sequence", width: "96px", fill: [nextSlot] }),
|
|
544
592
|
end: Sheet.column.date(PlanRowType, { header: "End", sub: "derive", width: "96px", base: "start", fill: [endFromStart] }),
|
|
545
593
|
activity: Sheet.column.lookup(PlanRowType, { header: "Activity", sub: "activity register", width: "214px" }),
|
|
546
594
|
qty: Sheet.column.quantity(PlanRowType, ActivityType, { header: "Qty", sub: "history · default", width: "112px", uom: d => d.uom, fill: [lastQuantity, shiftQuantity] }),
|
|
547
595
|
notes: Sheet.column.text(PlanRowType, { header: "Notes", width: "260px" }),
|
|
548
|
-
}, suggest: { ahead: 2n, triggers: ["activity", "start", "end", "qty"], propose: [roughingFollowUps, modelProposals, lastFollower] },
|
|
596
|
+
}, suggest: { ahead: 2n, triggers: ["activity", "start", "end", "qty"], propose: [roughingFollowUps, modelProposals, lastFollower] }, onPatch: onPatch, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(PlanRowType), () => Sheet.patch(PlanRowType, {
|
|
597
|
+
notes: "", stations: { from: [], to: [] }, fromSite: "", toSite: "", orderCode: "", status: "PLANNED",
|
|
598
|
+
})), onUpdate: plan.write, style: { height: "420px" } }), _jsx(Text.MonoLabel, { children: East.str `COPILOT LOG · ${entries.length()} edits · ${entries.filter((_$, l) => l.startsWith("typed").not()).length()} from the copilot` })] }));
|
|
549
599
|
} }))),
|
|
550
600
|
inputs: [],
|
|
551
601
|
});
|
|
@@ -592,9 +642,9 @@ export const sheetLens = example({
|
|
|
592
642
|
}));
|
|
593
643
|
const slice = $.let(Slice.bind([JobType], "sheet_lens_slice", cfg, Slice.state(), rows, none));
|
|
594
644
|
const views = $.let(State.bind([ArrayType(Sheet.Types.View)], "sheet_lens_views", [
|
|
595
|
-
{ id: "painting", name: "PAINTING", narrowing: Slice.state({ search: some("painting") }), context: 1n, reveals: [] },
|
|
596
|
-
{ id: "lathes", name: "LATHES", narrowing: Slice.state({ search: some("lathe") }), context: 0n, reveals: [] },
|
|
597
|
-
{ id: "urgent", name: "URGENT", narrowing: Slice.state({ search: some("urgent") }), context: 0n, reveals: [] },
|
|
645
|
+
{ id: "painting", name: "PAINTING", narrowing: Slice.state({ search: some("painting") }), context: 1n, reveals: [], folds: new Map() },
|
|
646
|
+
{ id: "lathes", name: "LATHES", narrowing: Slice.state({ search: some("lathe") }), context: 0n, reveals: [], folds: new Map() },
|
|
647
|
+
{ id: "urgent", name: "URGENT", narrowing: Slice.state({ search: some("urgent") }), context: 0n, reveals: [], folds: new Map() },
|
|
598
648
|
]));
|
|
599
649
|
return (_jsx(Sheet, { data: rows, id: "id", registers: {
|
|
600
650
|
stations: Sheet.register.concat([
|
|
@@ -614,13 +664,13 @@ export const sheetLens = example({
|
|
|
614
664
|
inputs: [],
|
|
615
665
|
});
|
|
616
666
|
/**
|
|
617
|
-
* Write-back (§3.7) —
|
|
618
|
-
*
|
|
619
|
-
*
|
|
667
|
+
* Write-back (§3.7) — onPatch journals one complete gesture, while the
|
|
668
|
+
* live onUpdate adapter checks and applies the composed batch once. The
|
|
669
|
+
* Sheet holds local drafts until Apply changes is pressed.
|
|
620
670
|
*/
|
|
621
671
|
export const sheetWriteBack = example({
|
|
622
|
-
keywords: ["Sheet", "Root", "
|
|
623
|
-
description: "Write-back —
|
|
672
|
+
keywords: ["Sheet", "Root", "onPatch", "onUpdate", "write-back", "commit", "insert", "remove", "source", "provenance", "PatchEvent", "staged", "newRowId", "Reactive", "State"],
|
|
673
|
+
description: "Write-back — one onPatch event per gesture with provenance and readiness, beside onUpdate applying a checked batch to the live collection",
|
|
624
674
|
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
625
675
|
const JobType = StructType({ id: StringType, task: StringType, qty: OptionType(FloatType), createdBy: StringType });
|
|
626
676
|
const jobs = $.let(State.bind([ArrayType(JobType)], "sheet_writeback_rows", [
|
|
@@ -629,15 +679,10 @@ export const sheetWriteBack = example({
|
|
|
629
679
|
]));
|
|
630
680
|
const log = $.let(State.bind([ArrayType(StringType)], "sheet_writeback_log", []));
|
|
631
681
|
const counter = $.let(State.bind([IntegerType], "sheet_writeback_counter", 3n));
|
|
632
|
-
//
|
|
633
|
-
//
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
commit: (_$, c) => East.str `commit ${c.rowId}.${c.key} (${c.source.getTag()}) · ${c.row.task} · by ${c.row.createdBy}`,
|
|
637
|
-
insert: (_$, i) => East.str `insert ${i.row.id} after ${i.afterRowId.match({ some: (_$2, a) => a, none: (_$2) => "end" })}`,
|
|
638
|
-
remove: (_$, r) => East.str `remove ${r.rowIds.stringJoin(", ")}`,
|
|
639
|
-
}));
|
|
640
|
-
$(log.write(log.read().concat([line])));
|
|
682
|
+
// Draft patches include hidden fields without inventing their values.
|
|
683
|
+
// This observer journals gestures; the live adapter applies ready batches.
|
|
684
|
+
const onPatch = $.const(East.function([Sheet.Types.PatchEvent(JobType)], NullType, ($, e) => {
|
|
685
|
+
$(log.write(log.read().concat([East.str `${e.origin.getTag()} · ${e.label} · ${e.readiness.getTag()}`])));
|
|
641
686
|
}));
|
|
642
687
|
// The host mints ids for inserted rows.
|
|
643
688
|
const newRowId = $.const(East.function([], StringType, ($) => {
|
|
@@ -646,10 +691,48 @@ export const sheetWriteBack = example({
|
|
|
646
691
|
return East.str `j${n}`;
|
|
647
692
|
}));
|
|
648
693
|
const entries = $.let(log.read());
|
|
649
|
-
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: jobs
|
|
694
|
+
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: jobs, id: "id", columns: {
|
|
650
695
|
task: Sheet.column.text(JobType, { header: "Task", width: "200px" }),
|
|
651
696
|
qty: Sheet.column.quantity(JobType, { header: "Qty", width: "112px" }),
|
|
652
|
-
},
|
|
697
|
+
}, onPatch: onPatch, onUpdate: jobs.write, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { createdBy: "planner" })), newRowId: newRowId, blanks: 6n }), _jsx(Text.MonoLabel, { children: East.str `EDITS · ${entries.length()}` }), entries.map((_$, l) => _jsx(Text, { textStyle: "caption", color: "fg.muted", children: l }))] }));
|
|
698
|
+
} }))),
|
|
699
|
+
inputs: [],
|
|
700
|
+
});
|
|
701
|
+
/**
|
|
702
|
+
* Grouped editing — named work packages with ordered child rows. Explicit
|
|
703
|
+
* constructors supply hidden metadata; the schema determines completeness.
|
|
704
|
+
*/
|
|
705
|
+
export const sheetGrouped = example({
|
|
706
|
+
keywords: ["Sheet", "group", "grouped", "children", "newGroup", "newRow", "Patch", "defaults", "completeness", "fold", "undo", "redo", "onUpdate", "Reactive", "State"],
|
|
707
|
+
description: "Grouped work packages — edit names and child rows, fold groups, create drafts with explicit defaults, then apply or undo the batch",
|
|
708
|
+
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
709
|
+
const JobType = StructType({ task: StringType, qty: OptionType(FloatType), notes: StringType, createdBy: StringType });
|
|
710
|
+
const PackageType = StructType({ id: StringType, name: StringType, owner: StringType, jobs: ArrayType(JobType) });
|
|
711
|
+
const packages = $.let(State.bind([ArrayType(PackageType)], "sheet_grouped_packages", [
|
|
712
|
+
{ id: "roughing", name: "P-40 · Roughing", owner: "planner", jobs: [
|
|
713
|
+
{ task: "Machine blanks", qty: some(1200.0), notes: "Four CNC lathes", createdBy: "planner" },
|
|
714
|
+
{ task: "Inspect lots", qty: some(4.0), notes: "Check before finishing", createdBy: "planner" },
|
|
715
|
+
] },
|
|
716
|
+
{ id: "finishing", name: "P-40 · Finishing", owner: "planner", jobs: [
|
|
717
|
+
{ task: "Finish housings", qty: some(1200.0), notes: "After inspection", createdBy: "planner" },
|
|
718
|
+
{ task: "Pack for assembly", qty: some(100.0), notes: "Twelve per carton", createdBy: "planner" },
|
|
719
|
+
] },
|
|
720
|
+
]));
|
|
721
|
+
const applied = $.let(packages.read());
|
|
722
|
+
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { textStyle: "caption", color: "fg.muted", children: "Edit a package name or task. Use the gutter + to insert a task, or the stacked-rows + button to create a package at a group boundary. Apply changes saves the batch; Undo and Redo retain each gesture." }), _jsx(Sheet, { data: packages, id: "id", group: Sheet.group(PackageType, "jobs", { title: "name" }), ready: { group: East.function([Sheet.Types.DraftGroup(PackageType, "jobs")], Sheet.Types.Readiness, ($, group) => {
|
|
723
|
+
$.if(group.name.hasTag("value").and(() => group.name.unwrap("value").length().equal(0n)), $ => {
|
|
724
|
+
$.return(East.value(variant("incomplete", [{ field: "name", message: "Name the work package" }]), Sheet.Types.Readiness));
|
|
725
|
+
});
|
|
726
|
+
return East.value(variant("ready", null), Sheet.Types.Readiness);
|
|
727
|
+
}) }, columns: {
|
|
728
|
+
task: Sheet.column.text(JobType, { header: "Task", width: "240px" }),
|
|
729
|
+
qty: Sheet.column.quantity(JobType, { header: "Qty", width: "112px" }),
|
|
730
|
+
notes: Sheet.column.text(JobType, { header: "Notes", width: "300px" }),
|
|
731
|
+
}, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, {
|
|
732
|
+
notes: "", createdBy: "planner",
|
|
733
|
+
})), newGroup: East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(PackageType), () => Sheet.patch(PackageType, {
|
|
734
|
+
owner: "planner", jobs: [],
|
|
735
|
+
})), onUpdate: packages.write, style: { height: "440px" } }), _jsx(Text.MonoLabel, { children: East.str `SAVED · ${applied.length()} packages · ${applied.map((_$, p) => p.jobs.length()).sum()} tasks` })] }));
|
|
653
736
|
} }))),
|
|
654
737
|
inputs: [],
|
|
655
738
|
});
|
|
@@ -657,11 +740,11 @@ export const sheetWriteBack = example({
|
|
|
657
740
|
* The paged arm (§3.13) — the same tag over a windowed source: windows land
|
|
658
741
|
* as the planner scrolls, the footer carries the transport line, the blank
|
|
659
742
|
* tail appears once the source is exhausted, search is a key search over
|
|
660
|
-
* `seek`, and
|
|
743
|
+
* `seek`, and the immutable snapshot is read-only. A mutable source needs an authoritative onApply callback.
|
|
661
744
|
*/
|
|
662
745
|
export const sheetPaged = example({
|
|
663
|
-
keywords: ["Sheet", "Root", "paged", "Paged", "of", "window", "page", "seek", "key search", "transport", "partial", "exhausted", "
|
|
664
|
-
description: "A paged sheet — the same tag over a Paged.of source keyed by id: windows land on scroll, the footer counts elements, key search seeks the source, and
|
|
746
|
+
keywords: ["Sheet", "Root", "paged", "Paged", "of", "window", "page", "seek", "key search", "transport", "partial", "exhausted", "onPatch", "journal", "row-source", "Reactive", "State"],
|
|
747
|
+
description: "A paged sheet — the same tag over a Paged.of source keyed by id: windows land on scroll, the footer counts elements, key search seeks the source, and its immutable snapshot stays read-only",
|
|
665
748
|
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
666
749
|
const JobType = StructType({ id: StringType, start: OptionType(DateTimeType), task: StringType, qty: OptionType(FloatType) });
|
|
667
750
|
// Six hundred rows behind a source that windows them — keyed by id,
|
|
@@ -673,16 +756,11 @@ export const sheetPaged = example({
|
|
|
673
756
|
qty: some(i.multiply(15n).toFloat().add(180.0)),
|
|
674
757
|
})), ArrayType(JobType));
|
|
675
758
|
const source = $.const(Paged.of("sheet_paged_jobs", rows, { key: r => r.id })); // Data.bindPaged(planInput) in e3-ui
|
|
676
|
-
const edits = $.let(State.bind([ArrayType(Sheet.Types.Edit(JobType))], "sheet_paged_edits", []));
|
|
677
|
-
const applyEdit = $.const(East.function([Sheet.Types.Edit(JobType)], NullType, ($, e) => {
|
|
678
|
-
$(edits.write(edits.read().concat([e]))); // the host's edit journal, replayed server-side
|
|
679
|
-
}));
|
|
680
|
-
const journal = $.let(edits.read());
|
|
681
759
|
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Sheet, { data: source, id: "id", columns: {
|
|
682
760
|
start: Sheet.column.date(JobType, { header: "Start", width: "96px" }),
|
|
683
761
|
task: Sheet.column.text(JobType, { header: "Task", width: "180px" }),
|
|
684
762
|
qty: Sheet.column.quantity(JobType, { header: "Qty", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }) }),
|
|
685
|
-
},
|
|
763
|
+
}, style: { height: "420px" } }), _jsx(Text.MonoLabel, { children: "Immutable snapshot \u00B7 key search and paging" })] }));
|
|
686
764
|
} }))),
|
|
687
765
|
inputs: [],
|
|
688
766
|
});
|
|
@@ -695,7 +773,7 @@ export const sheetStress = example({
|
|
|
695
773
|
description: "Two thousand rows in one sheet — virtualised rows, typed columns, and a history fill that reads the whole resident sheet",
|
|
696
774
|
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
697
775
|
const JobType = StructType({ id: StringType, start: OptionType(DateTimeType), task: StringType, qty: OptionType(FloatType), notes: StringType });
|
|
698
|
-
const Ctx = Sheet.Types.
|
|
776
|
+
const Ctx = Sheet.Types.DraftContext(JobType);
|
|
699
777
|
const tasks = $.const(["Machining", "Painting", "Packaging", "Changeover", "Maintenance", "Receiving", "Shipping"], ArrayType(StringType));
|
|
700
778
|
const rows = $.let(State.bind([ArrayType(JobType)], "sheet_stress_rows", East.Array.generate(2000n, JobType, (_$, i) => ({
|
|
701
779
|
id: East.str `S${i}`,
|
|
@@ -707,18 +785,282 @@ export const sheetStress = example({
|
|
|
707
785
|
const FloatFill = OptionType(Sheet.Types.Fill(FloatType));
|
|
708
786
|
const lastQty = $.const(East.function([Ctx], FloatFill, ($, ctx) => {
|
|
709
787
|
const noFill = $.const(none, FloatFill);
|
|
710
|
-
const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.task.equal(ctx.row.task).and(() => r.qty.hasTag("some"))));
|
|
788
|
+
const similar = $.let(ctx.rows.slice(0n, ctx.rowIndex).filter((_$, r) => r.task.hasTag("value").and(() => ctx.row.task.hasTag("value")).and(() => r.task.unwrap("value").equal(ctx.row.task.unwrap("value"))).and(() => r.qty.hasTag("value")).and(() => r.qty.unwrap("value").hasTag("some"))));
|
|
711
789
|
return similar.length().equal(0n).ifElse((_$) => noFill, ($2) => {
|
|
712
790
|
const r = $2.let(similar.get(similar.length().subtract(1n)));
|
|
713
|
-
return East.value(some({ value: r.qty.unwrap("some"), meta: East.str `like ${r.id}` }), FloatFill);
|
|
791
|
+
return East.value(some({ value: r.qty.unwrap("value").unwrap("some"), meta: East.str `like ${r.id.match({ value: (_$, id) => id }, () => "previous row")}` }), FloatFill);
|
|
714
792
|
});
|
|
715
793
|
}));
|
|
716
|
-
return (_jsx(Sheet, { data: rows
|
|
794
|
+
return (_jsx(Sheet, { data: rows, id: "id", columns: {
|
|
717
795
|
start: Sheet.column.date(JobType, { header: "Start", width: "96px" }),
|
|
718
796
|
task: Sheet.column.text(JobType, { header: "Task", width: "160px" }),
|
|
719
797
|
qty: Sheet.column.quantity(JobType, { header: "Qty", width: "112px", format: Format.Number({ maximumFractionDigits: 0n }), fill: [lastQty] }),
|
|
720
798
|
notes: Sheet.column.text(JobType, { header: "Notes", width: "240px" }),
|
|
721
|
-
}, onUpdate: rows.write, footer: [{ text: "2000 rows · virtualised" }], style: { height: "480px" } }));
|
|
799
|
+
}, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { notes: "" })), onUpdate: rows.write, footer: [{ text: "2000 rows · virtualised" }], style: { height: "480px" } }));
|
|
800
|
+
} }))),
|
|
801
|
+
inputs: [],
|
|
802
|
+
});
|
|
803
|
+
/** Schema completeness plus an optional business rule, evaluated on typed drafts. */
|
|
804
|
+
export const sheetReadiness = example({
|
|
805
|
+
keywords: ["Sheet", "ready", "row", "Readiness", "Draft", "DraftContext", "completeness", "validation", "Apply", "onUpdate", "Reactive", "State"],
|
|
806
|
+
description: "Draft readiness — quantities must be positive before Apply; missing required values and invalid text are checked automatically",
|
|
807
|
+
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
808
|
+
const JobType = StructType({ id: StringType, task: StringType, qty: IntegerType, note: OptionType(StringType), createdBy: StringType });
|
|
809
|
+
const jobs = $.const(State.bind([ArrayType(JobType)], "sheet_readiness_jobs", [
|
|
810
|
+
{ id: "inspection", task: "Inspect lots", qty: 4n, note: none, createdBy: "planner" },
|
|
811
|
+
]));
|
|
812
|
+
const readyRow = $.const(East.function([Sheet.Types.Draft(JobType), Sheet.Types.DraftContext(JobType)], Sheet.Types.Readiness, ($, row) => {
|
|
813
|
+
$.if(row.qty.hasTag("value").and(() => row.qty.unwrap("value").lessEqual(0n)), $ => {
|
|
814
|
+
$.return(East.value(variant("incomplete", [{ field: "qty", message: "Quantity must be positive" }]), Sheet.Types.Readiness));
|
|
815
|
+
});
|
|
816
|
+
return East.value(variant("ready", null), Sheet.Types.Readiness);
|
|
817
|
+
}));
|
|
818
|
+
const saved = $.const(jobs.read());
|
|
819
|
+
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { textStyle: "caption", color: "fg.muted", children: "Set Qty to 0: Apply stays disabled. Enter a positive quantity to save. Required fields are checked automatically; Notes may stay blank." }), _jsx(Sheet, { data: jobs, id: "id", columns: { task: Sheet.column.text(JobType), qty: Sheet.column.integer(JobType), note: Sheet.column.text(JobType) }, ready: { row: readyRow }, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { createdBy: "planner" })), onUpdate: jobs.write, blanks: 2n }), _jsx(Text.MonoLabel, { children: East.str `SAVED · ${saved.length()} rows · ${saved.map((_$, row) => row.qty).sum()} total quantity` })] }));
|
|
820
|
+
} }))),
|
|
821
|
+
inputs: [],
|
|
822
|
+
});
|
|
823
|
+
/** Row insertion and capability limits over a live collection. */
|
|
824
|
+
export const sheetInsertion = example({
|
|
825
|
+
keywords: ["Sheet", "edits", "insertRows", "removeRows", "insert", "newRow", "before", "after", "Undo", "State"],
|
|
826
|
+
description: "Insert rows with the gutter buttons, selection strip or Alt+Insert; existing-row deletion is disabled while new drafts can be discarded",
|
|
827
|
+
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
828
|
+
const RowType = StructType({ id: StringType, task: StringType, qty: IntegerType, createdBy: StringType });
|
|
829
|
+
const jobs = $.const(State.bind([ArrayType(RowType)], "sheet_insertion_jobs", [
|
|
830
|
+
{ id: "rough", task: "Rough machining", qty: 120n, createdBy: "planner" },
|
|
831
|
+
{ id: "inspect", task: "Inspect lots", qty: 4n, createdBy: "planner" },
|
|
832
|
+
{ id: "finish", task: "Finish housings", qty: 120n, createdBy: "planner" },
|
|
833
|
+
]));
|
|
834
|
+
const saved = $.const(jobs.read());
|
|
835
|
+
return _jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { textStyle: "caption", color: "fg.muted", children: "Hover or focus a gutter to insert before a row. Select a row marker for Insert above/below, or use Alt+Insert and Alt+Shift+Insert. Name the draft and Apply to save it." }), _jsx(Sheet, { data: jobs, id: "id", columns: { task: Sheet.column.text(RowType), qty: Sheet.column.integer(RowType) }, edits: { insertRows: true, removeRows: false, moveRows: "none" }, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(RowType), () => Sheet.patch(RowType, { qty: 1n, createdBy: "planner" })), onUpdate: jobs.write, blanks: 2n }), _jsx(Text.MonoLabel, { children: East.str `SAVED · ${saved.map((_$, row) => row.task).stringJoin(" → ")}` })] });
|
|
836
|
+
} }))),
|
|
837
|
+
inputs: [],
|
|
838
|
+
});
|
|
839
|
+
/**
|
|
840
|
+
* Sub rows (#844) — read-only rows under each line that share none of its
|
|
841
|
+
* columns. `Sheet.subRows` is keyed like `columns`: each key names an array
|
|
842
|
+
* field of the line type and maps one element to a sub row — a struct source
|
|
843
|
+
* with chips and labelled facets (a `none` facet drops out), and a variant
|
|
844
|
+
* source matched arm by arm. The group's noun is the host's word.
|
|
845
|
+
*/
|
|
846
|
+
export const sheetSubRows = example({
|
|
847
|
+
keywords: ["Sheet", "subRows", "subRow", "sub rows", "SubRow", "Facet", "chips", "facets", "lead", "detail", "group", "noun", "variant", "match", "read-only", "Reactive", "State"],
|
|
848
|
+
description: "Sub rows under each line — operations with chips and facets, bookings matched from a variant — declared per array field like columns, under groups the host calls orders",
|
|
849
|
+
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
850
|
+
const OperationType = StructType({
|
|
851
|
+
id: StringType, code: StringType, name: StringType, materials: ArrayType(StringType),
|
|
852
|
+
station: OptionType(StringType), by: OptionType(StringType),
|
|
853
|
+
});
|
|
854
|
+
const BookingType = VariantType({
|
|
855
|
+
space: StructType({ area: StringType, units: IntegerType }),
|
|
856
|
+
labour: StructType({ team: StringType, people: IntegerType, hours: FloatType }),
|
|
857
|
+
equipment: StructType({ resource: StringType }),
|
|
858
|
+
});
|
|
859
|
+
const JobType = StructType({
|
|
860
|
+
task: StringType, qty: OptionType(FloatType), notes: StringType,
|
|
861
|
+
operations: ArrayType(OperationType), // no column — shown as sub rows
|
|
862
|
+
bookings: ArrayType(BookingType), // no column — shown as sub rows
|
|
863
|
+
});
|
|
864
|
+
const OrderType = StructType({ id: StringType, name: StringType, jobs: ArrayType(JobType) });
|
|
865
|
+
const orders = $.let(State.bind([ArrayType(OrderType)], "sheet_subrows_orders", [
|
|
866
|
+
{ id: "wo-1042", name: "WO-1042 · Frames", jobs: [
|
|
867
|
+
{ task: "Assemble frames", qty: some(40.0), notes: "Two benches", operations: [
|
|
868
|
+
{ id: "WO-1042-1", code: "CUT", name: "Cut rails to length", materials: ["Rail stock × 80"], station: some("Saw 2"), by: none },
|
|
869
|
+
{ id: "WO-1042-2", code: "ASM", name: "Assemble frame", materials: ["M6 bolts × 12", "Frame kit"], station: some("Bench 7"), by: some("Assembly") },
|
|
870
|
+
], bookings: [
|
|
871
|
+
variant("labour", { team: "Assembly", people: 2n, hours: 12.0 }),
|
|
872
|
+
variant("equipment", { resource: "Torque driver" }),
|
|
873
|
+
] },
|
|
874
|
+
{ task: "Inspect frames", qty: some(40.0), notes: "", operations: [], bookings: [
|
|
875
|
+
variant("space", { area: "Test bay", units: 2n }),
|
|
876
|
+
] },
|
|
877
|
+
] },
|
|
878
|
+
{ id: "wo-1043", name: "WO-1043 · Housings", jobs: [
|
|
879
|
+
{ task: "Paint housings", qty: some(250.0), notes: "Primer first", operations: [
|
|
880
|
+
{ id: "WO-1043-1", code: "PNT", name: "Prime and paint", materials: ["Primer", "Topcoat"], station: none, by: none },
|
|
881
|
+
], bookings: [] },
|
|
882
|
+
] },
|
|
883
|
+
]));
|
|
884
|
+
return (_jsx(Sheet, { data: orders, id: "id", group: Sheet.group(OrderType, "jobs", { title: "name", noun: { singular: "order", plural: "orders" } }), columns: {
|
|
885
|
+
task: Sheet.column.text(JobType, { header: "Task", width: "220px" }),
|
|
886
|
+
qty: Sheet.column.quantity(JobType, { header: "Qty", width: "96px" }),
|
|
887
|
+
notes: Sheet.column.text(JobType, { header: "Notes", width: "240px" }),
|
|
888
|
+
}, subRows: Sheet.subRows(JobType, {
|
|
889
|
+
operations: (op) => Sheet.subRow({
|
|
890
|
+
code: op.code,
|
|
891
|
+
name: op.name,
|
|
892
|
+
chips: op.materials,
|
|
893
|
+
facets: { station: op.station, by: op.by }, // a none drops out
|
|
894
|
+
id: op.id,
|
|
895
|
+
}),
|
|
896
|
+
bookings: (b) => b.match({
|
|
897
|
+
space: (_$2, s) => Sheet.subRow({ code: "CLAIM", name: East.str `${s.area} · ${s.units} units` }),
|
|
898
|
+
labour: (_$2, l) => Sheet.subRow({ code: "LABOUR", name: East.str `${l.team} · ${l.people} people · ${l.hours} person-hours` }),
|
|
899
|
+
equipment: (_$2, e) => Sheet.subRow({ code: "EQUIPMENT", name: e.resource }),
|
|
900
|
+
}),
|
|
901
|
+
}), newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { notes: "", operations: [], bookings: [] })), newGroup: East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(OrderType), () => Sheet.patch(OrderType, { jobs: [] })), onUpdate: orders.write, style: { height: "420px" } }));
|
|
902
|
+
} }))),
|
|
903
|
+
inputs: [],
|
|
904
|
+
});
|
|
905
|
+
/**
|
|
906
|
+
* Column rules (#844) — what a column offers, how deep a date reads, and
|
|
907
|
+
* what a cell says beyond its value: an `options` rule narrows the status
|
|
908
|
+
* menu until the row has an order code; the start date reads at the row's
|
|
909
|
+
* own `level` and prints the `actual` start once one is recorded; the status
|
|
910
|
+
* cell's `detail` is the order code; machines are a `ranged` kind, so runs
|
|
911
|
+
* of consecutive codes are offered and printed as one range.
|
|
912
|
+
*/
|
|
913
|
+
export const sheetRules = example({
|
|
914
|
+
keywords: ["Sheet", "options", "level", "actual", "detail", "ranged", "DateLevel", "week", "day", "range", "time", "enum", "date", "set", "members", "rule", "Reactive", "State"],
|
|
915
|
+
description: "Column rules — an options rule narrowing an enum's menu, a date read at each row's level with its actual start, a status detail, and a ranged machine kind",
|
|
916
|
+
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
917
|
+
const JobType = StructType({
|
|
918
|
+
id: StringType, task: StringType, start: OptionType(DateTimeType), level: Sheet.Types.DateLevel,
|
|
919
|
+
started: OptionType(DateTimeType), status: StringType, orderCode: StringType, machines: Sheet.Types.Link,
|
|
920
|
+
});
|
|
921
|
+
const Ctx = Sheet.Types.DraftContext(JobType);
|
|
922
|
+
const StatusType = StructType({ word: StringType, tone: Status.Types.Value });
|
|
923
|
+
const statuses = $.const([
|
|
924
|
+
{ word: "PLANNED", tone: variant("neutral", null) }, { word: "RELEASED", tone: variant("info", null) },
|
|
925
|
+
{ word: "IN PROGRESS", tone: variant("warning", null) }, { word: "COMPLETE", tone: variant("success", null) },
|
|
926
|
+
], ArrayType(StatusType));
|
|
927
|
+
const machines = $.const(["M2140", "M2141", "M2142", "M2143", "M3210", "M3211"], ArrayType(StringType));
|
|
928
|
+
const noMembers = $.const([], ArrayType(Sheet.Types.Member));
|
|
929
|
+
const jobs = $.let(State.bind([ArrayType(JobType)], "sheet_rules_jobs", [
|
|
930
|
+
{ id: "j1", task: "Rough blanks", start: some(new Date("2026-02-16T00:00:00Z")), level: variant("week", null), started: none, status: "PLANNED", orderCode: "", machines: { from: [], to: [] } },
|
|
931
|
+
{ id: "j2", task: "Finish blanks", start: some(new Date("2026-02-18T00:00:00Z")), level: variant("day", null), started: some(new Date("2026-02-19T07:30:00Z")), status: "IN PROGRESS", orderCode: "WO-26001", machines: { from: [], to: [variant("range", { from: "M2140", to: "M2143" })] } },
|
|
932
|
+
{ id: "j3", task: "Changeover", start: some(new Date("2026-02-19T14:00:00Z")), level: variant("time", null), started: none, status: "RELEASED", orderCode: "WO-26002", machines: { from: [], to: [] } },
|
|
933
|
+
]));
|
|
934
|
+
// Until a row has an order code it may only be planned or released.
|
|
935
|
+
const statusOptions = $.const(East.function([Ctx], OptionType(ArrayType(StringType)), ($, ctx) => {
|
|
936
|
+
const whole = $.const(none, OptionType(ArrayType(StringType)));
|
|
937
|
+
const early = $.const(some(["PLANNED", "RELEASED"]), OptionType(ArrayType(StringType)));
|
|
938
|
+
return ctx.row.orderCode.match({ value: (_$2, code) => code.length().equal(0n).ifElse(() => early, () => whole) }, () => early);
|
|
939
|
+
}));
|
|
940
|
+
return (_jsx(Sheet, { data: jobs, id: "id", registers: {
|
|
941
|
+
statuses: Sheet.register.members(statuses, { kind: "status", key: s => s.word, label: s => s.word, tone: s => some(s.tone) }),
|
|
942
|
+
machines: Sheet.register.members(machines, { kind: "machine", key: m => m, label: m => m }),
|
|
943
|
+
}, columns: {
|
|
944
|
+
task: Sheet.column.text(JobType, { header: "Task", width: "180px" }),
|
|
945
|
+
start: Sheet.column.date(JobType, { header: "Start", sub: "at the row's level", width: "168px", level: r => r.level, actual: r => r.started }),
|
|
946
|
+
status: Sheet.column.enum(JobType, "statuses", { header: "Status", width: "132px", options: statusOptions, detail: r => r.orderCode }),
|
|
947
|
+
machines: Sheet.column.set(JobType, "machines", { header: "Machines", sub: "M2140-43 · a run", width: "220px",
|
|
948
|
+
members: [{ kind: "machine", identified: true, ranged: true }] }),
|
|
949
|
+
}, newRow: East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, {
|
|
950
|
+
level: variant("day", null), started: none, orderCode: "", machines: { from: noMembers, to: noMembers },
|
|
951
|
+
})), onUpdate: jobs.write, blanks: 4n }));
|
|
952
|
+
} }))),
|
|
953
|
+
inputs: [],
|
|
954
|
+
});
|
|
955
|
+
/**
|
|
956
|
+
* Registers and the driver (§3.3) — a register is the grammar's vocabulary,
|
|
957
|
+
* projected from the host's rows by accessors: machines from an Array with a
|
|
958
|
+
* meta and a parent, lines from a Dict whose key rides as the accessors'
|
|
959
|
+
* second argument, and the machine families read off the same machines (the
|
|
960
|
+
* lathes fold into one member), joined into the one register the Machines
|
|
961
|
+
* column resolves against. The driver is the `lookup` column whose member
|
|
962
|
+
* decides what the row does: its row gives the quantity its unit and the End
|
|
963
|
+
* fill its duration.
|
|
964
|
+
*/
|
|
965
|
+
export const sheetRegisters = example({
|
|
966
|
+
keywords: ["Sheet", "register", "members", "concat", "driver", "lookup", "set", "kind", "key", "label", "aliases", "meta", "parent", "Dict", "fold", "duplicate", "uom", "fill", "DraftContext", "Reactive", "State"],
|
|
967
|
+
description: "Registers and the driver — members projected from Array and Dict rows (meta, parent, aliases, duplicate keys folded) and joined into one register a set column resolves against, beside a driver whose row gives each quantity its unit and a fill its duration",
|
|
968
|
+
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
969
|
+
const JobType = StructType({
|
|
970
|
+
id: StringType, activity: StringType, start: OptionType(DateTimeType), end: OptionType(DateTimeType),
|
|
971
|
+
qty: OptionType(FloatType), machines: Sheet.Types.Link,
|
|
972
|
+
});
|
|
973
|
+
const ActivityType = StructType({ name: StringType, uom: StringType, days: IntegerType });
|
|
974
|
+
const MachineType = StructType({ code: StringType, family: StringType, line: StringType });
|
|
975
|
+
const LineType = StructType({ name: StringType, aliases: ArrayType(StringType) });
|
|
976
|
+
const activities = $.const([
|
|
977
|
+
{ name: "Machining", uom: "pcs", days: 4n },
|
|
978
|
+
{ name: "Inspection", uom: "lots", days: 1n },
|
|
979
|
+
], ArrayType(ActivityType));
|
|
980
|
+
const machines = $.const([
|
|
981
|
+
{ code: "M2140", family: "CNC lathe", line: "L2" },
|
|
982
|
+
{ code: "M2141", family: "CNC lathe", line: "L2" },
|
|
983
|
+
{ code: "M3210", family: "5-axis mill", line: "L3" },
|
|
984
|
+
], ArrayType(MachineType));
|
|
985
|
+
const lines = $.const(new Map([
|
|
986
|
+
["L2", { name: "Line 2", aliases: ["l2", "line 2"] }],
|
|
987
|
+
["L3", { name: "Line 3", aliases: ["l3", "line 3"] }],
|
|
988
|
+
]), DictType(StringType, LineType));
|
|
989
|
+
const jobs = $.let(State.bind([ArrayType(JobType)], "sheet_registers_jobs", [
|
|
990
|
+
{ id: "j1", activity: "Machining", start: some(new Date("2026-02-16T00:00:00Z")), end: none, qty: some(1200.0),
|
|
991
|
+
machines: { from: [], to: [variant("counted", { n: 2n, key: "CNC lathe" })] } },
|
|
992
|
+
]));
|
|
993
|
+
// The driver's row reaches a fill typed: End = Start + the activity's days.
|
|
994
|
+
const DateFill = OptionType(Sheet.Types.Fill(DateTimeType));
|
|
995
|
+
const endFromStart = $.const(East.function([Sheet.Types.DraftContext(JobType, ActivityType)], DateFill, ($, ctx) => {
|
|
996
|
+
const noFill = $.const(none, DateFill);
|
|
997
|
+
return ctx.row.start.match({
|
|
998
|
+
value: (_$, supplied) => supplied.match({
|
|
999
|
+
none: () => noFill,
|
|
1000
|
+
some: (_$, start) => ctx.driver.match({
|
|
1001
|
+
none: () => noFill,
|
|
1002
|
+
some: (_$, d) => some({ value: start.addDays(d.days), meta: East.str `+${d.days}d · ${d.name}` }),
|
|
1003
|
+
}),
|
|
1004
|
+
}),
|
|
1005
|
+
}, () => noFill);
|
|
1006
|
+
}));
|
|
1007
|
+
const newJob = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(JobType), () => Sheet.patch(JobType, { machines: { from: [], to: [] } })));
|
|
1008
|
+
return (_jsx(Sheet, { data: jobs, id: "id", driver: Sheet.driver("activity", activities, { key: a => a.name, label: a => a.name, meta: a => some(a.uom) }), registers: {
|
|
1009
|
+
machines: Sheet.register.concat([
|
|
1010
|
+
Sheet.register.members(machines, { kind: "machine", key: m => m.code, label: m => m.code,
|
|
1011
|
+
meta: m => some(m.family), parent: m => some(m.line) }),
|
|
1012
|
+
// A Dict's key rides as the accessors' second argument.
|
|
1013
|
+
Sheet.register.members(lines, { kind: "line", key: (_l, code) => code, label: l => l.name, aliases: l => l.aliases }),
|
|
1014
|
+
// Both lathes name one family — duplicate keys fold, the first wins.
|
|
1015
|
+
Sheet.register.members(machines, { kind: "family", key: m => m.family, label: m => m.family, meta: _m => some("family") }),
|
|
1016
|
+
]),
|
|
1017
|
+
}, columns: {
|
|
1018
|
+
activity: Sheet.column.lookup(JobType, { header: "Activity", width: "140px" }),
|
|
1019
|
+
start: Sheet.column.date(JobType, { header: "Start", width: "96px" }),
|
|
1020
|
+
end: Sheet.column.date(JobType, { header: "End", sub: "start + days", width: "96px", base: "start", fill: [endFromStart] }),
|
|
1021
|
+
qty: Sheet.column.quantity(JobType, ActivityType, { header: "Qty", sub: "uom per activity", width: "112px", uom: d => d.uom }),
|
|
1022
|
+
machines: Sheet.column.set(JobType, "machines", { header: "Machines", sub: "M2140 · 2 x lathe · line 2", width: "240px",
|
|
1023
|
+
members: [{ kind: "machine", identified: true }, { kind: "line", countable: true, resolvesTo: "machine" },
|
|
1024
|
+
{ kind: "family", countable: true, resolvesTo: "machine" }] }),
|
|
1025
|
+
}, newRow: newJob, onUpdate: jobs.write }));
|
|
1026
|
+
} }))),
|
|
1027
|
+
inputs: [],
|
|
1028
|
+
});
|
|
1029
|
+
/**
|
|
1030
|
+
* Loose rows between the groups (#846) — the source holds entries
|
|
1031
|
+
* `Sheet.Types.Entry(PackageType, "tasks")`: a work package with its tasks,
|
|
1032
|
+
* or a task that belongs to no package. A loose task draws as a plain row,
|
|
1033
|
+
* numbered in the packages' sequence; the seam above a package's band, or
|
|
1034
|
+
* beside a loose task, inserts a loose task, and a task's seam inserts a task
|
|
1035
|
+
* into its package. `id` names a field of both types: a loose task is an
|
|
1036
|
+
* entry, identified like a package.
|
|
1037
|
+
*/
|
|
1038
|
+
export const sheetLoose = example({
|
|
1039
|
+
keywords: ["Sheet", "group", "grouped", "loose", "ungrouped", "Entry", "Types.Entry", "variant", "entries", "insert", "newRow", "newGroup", "noun", "onUpdate", "Reactive", "State"],
|
|
1040
|
+
description: "Loose rows between the groups — entries of work packages and loose tasks: a loose task is a plain row numbered in the packages' sequence, inserted at the seam above a package or beside another loose task",
|
|
1041
|
+
fn: East.function([], UIComponentType, (_$) => (_jsx(Reactive, { children: $ => {
|
|
1042
|
+
const TaskType = StructType({ id: StringType, task: StringType, qty: OptionType(FloatType), notes: StringType });
|
|
1043
|
+
const PackageType = StructType({ id: StringType, name: StringType, tasks: ArrayType(TaskType) });
|
|
1044
|
+
const Entry = Sheet.Types.Entry(PackageType, "tasks");
|
|
1045
|
+
const entries = $.let(State.bind([ArrayType(Entry)], "sheet_loose_entries", [
|
|
1046
|
+
variant("row", { id: "brief", task: "Review the drawings", qty: none, notes: "Before any machining" }),
|
|
1047
|
+
variant("group", { id: "roughing", name: "P-40 · Roughing", tasks: [
|
|
1048
|
+
{ id: "rough-1", task: "Machine blanks", qty: some(1200.0), notes: "Four CNC lathes" },
|
|
1049
|
+
{ id: "rough-2", task: "Inspect lots", qty: some(4.0), notes: "Check before finishing" },
|
|
1050
|
+
] }),
|
|
1051
|
+
variant("row", { id: "handover", task: "Hand over to finishing", qty: none, notes: "" }),
|
|
1052
|
+
variant("group", { id: "finishing", name: "P-40 · Finishing", tasks: [
|
|
1053
|
+
{ id: "finish-1", task: "Finish housings", qty: some(1200.0), notes: "After inspection" },
|
|
1054
|
+
] }),
|
|
1055
|
+
]));
|
|
1056
|
+
const saved = $.let(entries.read());
|
|
1057
|
+
const newTask = $.const(East.function([Sheet.Types.NewRow], Sheet.Types.Patch(TaskType), () => Sheet.patch(TaskType, { notes: "" })));
|
|
1058
|
+
const newPackage = $.const(East.function([Sheet.Types.NewGroup], Sheet.Types.Patch(PackageType), () => Sheet.patch(PackageType, { tasks: [] })));
|
|
1059
|
+
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsx(Text, { textStyle: "caption", color: "fg.muted", children: "Hover the seam above a package, or beside a loose task, to insert a loose task; a task's seam inserts a task into its package. Apply changes saves the batch." }), _jsx(Sheet, { data: entries, id: "id", group: Sheet.group(PackageType, "tasks", { title: "name", noun: { singular: "package", plural: "packages" } }), columns: {
|
|
1060
|
+
task: Sheet.column.text(TaskType, { header: "Task", width: "240px" }),
|
|
1061
|
+
qty: Sheet.column.quantity(TaskType, { header: "Qty", width: "112px" }),
|
|
1062
|
+
notes: Sheet.column.text(TaskType, { header: "Notes", width: "280px" }),
|
|
1063
|
+
}, newRow: newTask, newGroup: newPackage, onUpdate: entries.write, style: { height: "420px" } }), _jsx(Text.MonoLabel, { children: East.str `SAVED · ${saved.filter((_$, e) => e.hasTag("group")).length()} packages · ${saved.filter((_$, e) => e.hasTag("row")).length()} loose tasks` })] }));
|
|
722
1064
|
} }))),
|
|
723
1065
|
inputs: [],
|
|
724
1066
|
});
|