foldkit 0.137.0 → 0.138.0

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.
@@ -0,0 +1,69 @@
1
+ import { Option } from 'effect';
2
+ /** Context passed to the `viewTransition` predicate before each live render.
3
+ *
4
+ * A View Transition animates between two states, so the predicate is given
5
+ * both. `previousModel` is the Model behind the DOM on screen, the state the
6
+ * browser is about to snapshot. `model` is the Model the pending render will
7
+ * paint. Direction is the pair: comparing the two says where the application
8
+ * is going and where it came from, with no route history kept in the Model.
9
+ *
10
+ * `previousModel` is always a real painted state rather than an `Option`. The
11
+ * predicate only runs once a Message has dirtied the Model, and the initial
12
+ * render completes before any Message is processed.
13
+ *
14
+ * `message` is the Message that dirtied the Model for this frame. The runtime
15
+ * coalesces a burst of dispatches into one render frame, so the predicate
16
+ * sees the last dirtying Message before the frame. */
17
+ export type ViewTransitionContext<Model, Message> = Readonly<{
18
+ previousModel: Model;
19
+ model: Model;
20
+ message: Message;
21
+ }>;
22
+ /** What the `viewTransition` predicate returns for a render. `false` renders
23
+ * plainly. `true` wraps the render in `document.startViewTransition`.
24
+ * `{ types }` additionally tags the transition so CSS can scope animations
25
+ * via `:active-view-transition-type(...)`. */
26
+ export type ViewTransitionDecision = boolean | Readonly<{
27
+ types: ReadonlyArray<string>;
28
+ }>;
29
+ /** Decides, per render, whether the DOM update should run inside a View
30
+ * Transition. The predicate is total over the Message union, so animation is
31
+ * opted into per Message: return `true` for the ones worth animating and
32
+ * `false` for everything else. */
33
+ export type ViewTransitionConfig<Model, Message> = (context: ViewTransitionContext<Model, Message>) => ViewTransitionDecision;
34
+ /** The slice of the DOM `ViewTransition` handle the runtime consumes. `ready`
35
+ * and `finished` are optional: the runtime only ever attaches rejection
36
+ * handlers to them, so a handle that omits them stays valid. */
37
+ export type ViewTransitionHandle = Readonly<{
38
+ updateCallbackDone: Promise<void>;
39
+ skipTransition: () => void;
40
+ ready?: Promise<void>;
41
+ finished?: Promise<void>;
42
+ }>;
43
+ /**
44
+ * Attaches no-op rejection handlers to a transition's promises.
45
+ *
46
+ * The browser rejects `ready` whenever a transition is skipped, and skipping
47
+ * is routine rather than exceptional: a second navigation lands mid-animation,
48
+ * the document is hidden, or the runtime skips the transition itself while
49
+ * tearing down. The runtime never awaits any of these promises, so without a
50
+ * handler every skip surfaces as an unhandled rejection and lands in whatever
51
+ * the application wired to `window.onunhandledrejection`, turning a purely
52
+ * cosmetic animation into an error report.
53
+ */
54
+ export declare const __silenceViewTransitionRejections: (handle: ViewTransitionHandle) => void;
55
+ /** Starts a View Transition around `update`. Injected into the render path so
56
+ * tests can fake the browser API. */
57
+ export type StartViewTransition = (update: () => void, maybeTypes: Option.Option<ReadonlyArray<string>>) => ViewTransitionHandle;
58
+ /** Feature-detects `document.startViewTransition`, returning `Option.none()`
59
+ * where the API is unavailable so the runtime falls through to plain
60
+ * renders. Transition types are newer than the API itself, so when the
61
+ * running browser only supports the callback form the types are dropped and
62
+ * the transition still runs untyped. */
63
+ export declare const __resolveStartViewTransition: () => Option.Option<StartViewTransition>;
64
+ /** Normalizes a `ViewTransitionDecision` into "skip" (`Option.none()`) or
65
+ * "transition, with these types" so the render path branches once. */
66
+ export declare const __decideViewTransition: <Model, Message>(decide: ViewTransitionConfig<Model, Message>, context: ViewTransitionContext<Model, Message>) => Option.Option<Readonly<{
67
+ maybeTypes: Option.Option<ReadonlyArray<string>>;
68
+ }>>;
69
+ //# sourceMappingURL=viewTransition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewTransition.d.ts","sourceRoot":"","sources":["../../src/runtime/viewTransition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAQ,MAAM,QAAQ,CAAA;AAE5C;;;;;;;;;;;;;;uDAcuD;AACvD,MAAM,MAAM,qBAAqB,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAC3D,aAAa,EAAE,KAAK,CAAA;IACpB,KAAK,EAAE,KAAK,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB,CAAC,CAAA;AAEF;;;+CAG+C;AAC/C,MAAM,MAAM,sBAAsB,GAC9B,OAAO,GACP,QAAQ,CAAC;IAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAA;AAE9C;;;mCAGmC;AACnC,MAAM,MAAM,oBAAoB,CAAC,KAAK,EAAE,OAAO,IAAI,CACjD,OAAO,EAAE,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC,KAC3C,sBAAsB,CAAA;AAE3B;;iEAEiE;AACjE,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,kBAAkB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB,CAAC,CAAA;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iCAAiC,GAC5C,QAAQ,oBAAoB,KAC3B,IAKF,CAAA;AAED;sCACsC;AACtC,MAAM,MAAM,mBAAmB,GAAG,CAChC,MAAM,EAAE,MAAM,IAAI,EAClB,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,KAC7C,oBAAoB,CAAA;AAEzB;;;;yCAIyC;AACzC,eAAO,MAAM,4BAA4B,QACnC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAoBpC,CAAA;AAEH;uEACuE;AACvE,eAAO,MAAM,sBAAsB,GAAI,KAAK,EAAE,OAAO,EACnD,QAAQ,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,EAC5C,SAAS,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC,KAC7C,MAAM,CAAC,MAAM,CACd,QAAQ,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,CAAC,CAa7D,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { Match, Option, pipe } from 'effect';
2
+ /**
3
+ * Attaches no-op rejection handlers to a transition's promises.
4
+ *
5
+ * The browser rejects `ready` whenever a transition is skipped, and skipping
6
+ * is routine rather than exceptional: a second navigation lands mid-animation,
7
+ * the document is hidden, or the runtime skips the transition itself while
8
+ * tearing down. The runtime never awaits any of these promises, so without a
9
+ * handler every skip surfaces as an unhandled rejection and lands in whatever
10
+ * the application wired to `window.onunhandledrejection`, turning a purely
11
+ * cosmetic animation into an error report.
12
+ */
13
+ export const __silenceViewTransitionRejections = (handle) => {
14
+ const ignore = () => { };
15
+ handle.ready?.catch(ignore);
16
+ handle.finished?.catch(ignore);
17
+ handle.updateCallbackDone.catch(ignore);
18
+ };
19
+ /** Feature-detects `document.startViewTransition`, returning `Option.none()`
20
+ * where the API is unavailable so the runtime falls through to plain
21
+ * renders. Transition types are newer than the API itself, so when the
22
+ * running browser only supports the callback form the types are dropped and
23
+ * the transition still runs untyped. */
24
+ export const __resolveStartViewTransition = () => {
25
+ if (typeof document.startViewTransition !== 'function') {
26
+ return Option.none();
27
+ }
28
+ const isTypesSupported = typeof ViewTransition !== 'undefined' &&
29
+ 'types' in ViewTransition.prototype;
30
+ return Option.some((update, maybeTypes) => pipe(maybeTypes, Option.filter(() => isTypesSupported), Option.match({
31
+ onNone: () => document.startViewTransition(update),
32
+ onSome: types => document.startViewTransition({ update, types: [...types] }),
33
+ })));
34
+ };
35
+ /** Normalizes a `ViewTransitionDecision` into "skip" (`Option.none()`) or
36
+ * "transition, with these types" so the render path branches once. */
37
+ export const __decideViewTransition = (decide, context) => Match.value(decide(context)).pipe(Match.withReturnType(), Match.when(false, () => Option.none()), Match.when(true, () => Option.some({ maybeTypes: Option.none() })), Match.orElse(({ types }) => Option.some({ maybeTypes: Option.some(types) })));
@@ -11,5 +11,5 @@ export const update = (model, message) => M.value(message).pipe(M.withReturnType
11
11
  const TEST_ID = 'attribute-host';
12
12
  export const testId = TEST_ID;
13
13
  export const view = (model, h) => {
14
- return h.div([h.DataAttribute('testid', TEST_ID), model.attribute], []);
14
+ return h.div([h.DataAttribute('testid', TEST_ID), model.attribute]);
15
15
  };
@@ -88,5 +88,5 @@ export const twoPanelView = (model, h) => {
88
88
  * pending Mount's args). The chosen `offset` flows through `ScrollList`'s
89
89
  * args and is observable on the rendered Mount marker. */
90
90
  export const scrollListView = (offset, h) => {
91
- return h.div([h.Class('scroll-list')], [h.div([h.Key('list'), h.OnMount(ScrollList({ offset }))], [])]);
91
+ return h.div([h.Class('scroll-list')], [h.div([h.Key('list'), h.OnMount(ScrollList({ offset }))])]);
92
92
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foldkit",
3
- "version": "0.137.0",
3
+ "version": "0.138.0",
4
4
  "description": "A TypeScript frontend framework, built on Effect and architected like Elm",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",