foldkit 0.120.0 → 0.121.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.
- package/dist/subscription/fromEvent.d.ts +78 -1
- package/dist/subscription/fromEvent.d.ts.map +1 -1
- package/dist/subscription/fromEvent.js +72 -9
- package/dist/subscription/public.d.ts +2 -2
- package/dist/subscription/public.d.ts.map +1 -1
- package/dist/subscription/public.js +1 -1
- package/dist/test/apps/{svgAttributes.d.ts → attributes.d.ts} +7 -6
- package/dist/test/apps/attributes.d.ts.map +1 -0
- package/dist/test/apps/attributes.js +17 -0
- package/package.json +1 -1
- package/dist/test/apps/svgAttributes.d.ts.map +0 -1
- package/dist/test/apps/svgAttributes.js +0 -106
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Stream } from 'effect';
|
|
1
|
+
import { Option, Stream } from 'effect';
|
|
2
2
|
/**
|
|
3
3
|
* Configuration for the `fromEvent` Stream helper.
|
|
4
4
|
*
|
|
@@ -18,6 +18,80 @@ export type FromEventConfig<EventType extends Event, Message> = Readonly<{
|
|
|
18
18
|
toMessage: (event: EventType) => Message;
|
|
19
19
|
options?: AddEventListenerOptions;
|
|
20
20
|
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Configuration for the `fromEventFilterMap` Stream helper.
|
|
23
|
+
*
|
|
24
|
+
* `target` is read inside the acquire Effect, never before it, so the
|
|
25
|
+
* resolved `EventTarget` is captured at the moment the Subscription's scope
|
|
26
|
+
* opens. Pass a thunk when the target may not exist until the scope opens, or
|
|
27
|
+
* pass the `EventTarget` directly for always-present globals like `window` or
|
|
28
|
+
* `document`.
|
|
29
|
+
*
|
|
30
|
+
* `toMessage(event)` returns `Option.some(message)` to emit a Message for the
|
|
31
|
+
* event, or `Option.none()` to ignore it. The mapper runs synchronously in the
|
|
32
|
+
* same call stack as the browser's event dispatch, so calling
|
|
33
|
+
* `event.preventDefault()` inside it works as expected.
|
|
34
|
+
*/
|
|
35
|
+
export type FromEventFilterMapConfig<EventType extends Event, Message> = Readonly<{
|
|
36
|
+
target: EventTarget | (() => EventTarget);
|
|
37
|
+
type: string;
|
|
38
|
+
toMessage: (event: EventType) => Option.Option<Message>;
|
|
39
|
+
options?: AddEventListenerOptions;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Build a Stream that emits a Message for the dispatches of a DOM event the
|
|
43
|
+
* mapper chooses to keep, registering the listener when the Stream's scope
|
|
44
|
+
* opens and removing it when the scope closes.
|
|
45
|
+
*
|
|
46
|
+
* This is the filtered variant of `fromEvent`. Its `toMessage` returns
|
|
47
|
+
* `Option.some(message)` to emit and `Option.none()` to ignore the event, so a
|
|
48
|
+
* single listener can react to some dispatches while passing on the rest.
|
|
49
|
+
*
|
|
50
|
+
* Reach for this over a downstream `Stream.filterMap` whenever the decision to
|
|
51
|
+
* keep an event is paired with `event.preventDefault()`. The mapper runs
|
|
52
|
+
* synchronously inside the browser's event dispatch, so `preventDefault()`
|
|
53
|
+
* takes effect, while a downstream filter would run on a later turn after the
|
|
54
|
+
* default action has already happened.
|
|
55
|
+
*
|
|
56
|
+
* The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
|
|
57
|
+
* call happens inside the acquire Effect, and the matching
|
|
58
|
+
* `removeEventListener` is registered only after acquire completes, so the
|
|
59
|
+
* listener never leaks on interruption.
|
|
60
|
+
*
|
|
61
|
+
* This is a Stream, not a Subscription entry. Wrap it with
|
|
62
|
+
* `Subscription.persistent` for a listener whose lifetime spans the whole
|
|
63
|
+
* Subscriptions record, or plug it into a `Subscription.make` entry's
|
|
64
|
+
* `dependenciesToStream` (typically behind `Stream.when`) to gate it on a
|
|
65
|
+
* Model condition.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const subscriptions = Subscription.make<Model, Message>()(entry => ({
|
|
70
|
+
* searchShortcut: entry(
|
|
71
|
+
* { isListening: S.Boolean },
|
|
72
|
+
* {
|
|
73
|
+
* modelToDependencies: model => ({ isListening: model.isListening }),
|
|
74
|
+
* dependenciesToStream: ({ isListening }) =>
|
|
75
|
+
* Stream.when(
|
|
76
|
+
* Subscription.fromEventFilterMap<KeyboardEvent, Message>({
|
|
77
|
+
* target: window,
|
|
78
|
+
* type: 'keydown',
|
|
79
|
+
* toMessage: event => {
|
|
80
|
+
* if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
|
|
81
|
+
* event.preventDefault()
|
|
82
|
+
* return Option.some(OpenedSearch())
|
|
83
|
+
* }
|
|
84
|
+
* return Option.none()
|
|
85
|
+
* },
|
|
86
|
+
* }),
|
|
87
|
+
* Effect.sync(() => isListening),
|
|
88
|
+
* ),
|
|
89
|
+
* },
|
|
90
|
+
* ),
|
|
91
|
+
* }))
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare const fromEventFilterMap: <EventType extends Event, Message>(config: FromEventFilterMapConfig<EventType, Message>) => Stream.Stream<Message>;
|
|
21
95
|
/**
|
|
22
96
|
* Build a Stream that emits a Message for every dispatch of a DOM event,
|
|
23
97
|
* registering the listener when the Stream's scope opens and removing it when
|
|
@@ -34,6 +108,9 @@ export type FromEventConfig<EventType extends Event, Message> = Readonly<{
|
|
|
34
108
|
* `dependenciesToStream` (typically behind `Stream.when`) to gate it on a
|
|
35
109
|
* Model condition.
|
|
36
110
|
*
|
|
111
|
+
* For a listener that reacts to only some events, reach for
|
|
112
|
+
* `fromEventFilterMap`, whose mapper returns `Option<Message>`.
|
|
113
|
+
*
|
|
37
114
|
* @example
|
|
38
115
|
* ```typescript
|
|
39
116
|
* const subscriptions = Subscription.make<Model, Message>()(entry => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fromEvent.d.ts","sourceRoot":"","sources":["../../src/subscription/fromEvent.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"fromEvent.d.ts","sourceRoot":"","sources":["../../src/subscription/fromEvent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,eAAe,CAAC,SAAS,SAAS,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IACvE,MAAM,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAA;IACxC,OAAO,CAAC,EAAE,uBAAuB,CAAA;CAClC,CAAC,CAAA;AAMF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,wBAAwB,CAClC,SAAS,SAAS,KAAK,EACvB,OAAO,IACL,QAAQ,CAAC;IACX,MAAM,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAA;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACvD,OAAO,CAAC,EAAE,uBAAuB,CAAA;CAClC,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,SAAS,KAAK,EAAE,OAAO,EACjE,QAAQ,wBAAwB,CAAC,SAAS,EAAE,OAAO,CAAC,KACnD,MAAM,CAAC,MAAM,CAAC,OAAO,CAsBrB,CAAA;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,SAAS,GAAI,SAAS,SAAS,KAAK,EAAE,OAAO,EACxD,QAAQ,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,KAC1C,MAAM,CAAC,MAAM,CAAC,OAAO,CAIpB,CAAA"}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import { Effect, Queue, Stream } from 'effect';
|
|
1
|
+
import { Effect, Option, Queue, Stream } from 'effect';
|
|
2
2
|
const resolveTarget = (target) => (typeof target === 'function' ? target() : target);
|
|
3
3
|
/**
|
|
4
|
-
* Build a Stream that emits a Message for
|
|
5
|
-
* registering the listener when the Stream's scope
|
|
6
|
-
* the scope closes.
|
|
4
|
+
* Build a Stream that emits a Message for the dispatches of a DOM event the
|
|
5
|
+
* mapper chooses to keep, registering the listener when the Stream's scope
|
|
6
|
+
* opens and removing it when the scope closes.
|
|
7
|
+
*
|
|
8
|
+
* This is the filtered variant of `fromEvent`. Its `toMessage` returns
|
|
9
|
+
* `Option.some(message)` to emit and `Option.none()` to ignore the event, so a
|
|
10
|
+
* single listener can react to some dispatches while passing on the rest.
|
|
11
|
+
*
|
|
12
|
+
* Reach for this over a downstream `Stream.filterMap` whenever the decision to
|
|
13
|
+
* keep an event is paired with `event.preventDefault()`. The mapper runs
|
|
14
|
+
* synchronously inside the browser's event dispatch, so `preventDefault()`
|
|
15
|
+
* takes effect, while a downstream filter would run on a later turn after the
|
|
16
|
+
* default action has already happened.
|
|
7
17
|
*
|
|
8
18
|
* The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
|
|
9
19
|
* call happens inside the acquire Effect, and the matching
|
|
@@ -19,16 +29,22 @@ const resolveTarget = (target) => (typeof target === 'function' ? target() : tar
|
|
|
19
29
|
* @example
|
|
20
30
|
* ```typescript
|
|
21
31
|
* const subscriptions = Subscription.make<Model, Message>()(entry => ({
|
|
22
|
-
*
|
|
32
|
+
* searchShortcut: entry(
|
|
23
33
|
* { isListening: S.Boolean },
|
|
24
34
|
* {
|
|
25
35
|
* modelToDependencies: model => ({ isListening: model.isListening }),
|
|
26
36
|
* dependenciesToStream: ({ isListening }) =>
|
|
27
37
|
* Stream.when(
|
|
28
|
-
* Subscription.
|
|
38
|
+
* Subscription.fromEventFilterMap<KeyboardEvent, Message>({
|
|
29
39
|
* target: window,
|
|
30
40
|
* type: 'keydown',
|
|
31
|
-
* toMessage: event =>
|
|
41
|
+
* toMessage: event => {
|
|
42
|
+
* if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
|
|
43
|
+
* event.preventDefault()
|
|
44
|
+
* return Option.some(OpenedSearch())
|
|
45
|
+
* }
|
|
46
|
+
* return Option.none()
|
|
47
|
+
* },
|
|
32
48
|
* }),
|
|
33
49
|
* Effect.sync(() => isListening),
|
|
34
50
|
* ),
|
|
@@ -37,14 +53,61 @@ const resolveTarget = (target) => (typeof target === 'function' ? target() : tar
|
|
|
37
53
|
* }))
|
|
38
54
|
* ```
|
|
39
55
|
*/
|
|
40
|
-
export const
|
|
56
|
+
export const fromEventFilterMap = (config) => Stream.callback(queue => Effect.acquireRelease(Effect.sync(() => {
|
|
41
57
|
const target = resolveTarget(config.target);
|
|
42
58
|
const handleEvent = (event) => {
|
|
43
59
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
44
|
-
|
|
60
|
+
const maybeMessage = config.toMessage(event);
|
|
61
|
+
if (Option.isSome(maybeMessage)) {
|
|
62
|
+
Queue.offerUnsafe(queue, maybeMessage.value);
|
|
63
|
+
}
|
|
45
64
|
};
|
|
46
65
|
target.addEventListener(config.type, handleEvent, config.options);
|
|
47
66
|
return { target, handleEvent };
|
|
48
67
|
}), ({ target, handleEvent }) => Effect.sync(() => {
|
|
49
68
|
target.removeEventListener(config.type, handleEvent, config.options);
|
|
50
69
|
})).pipe(Effect.flatMap(() => Effect.never)));
|
|
70
|
+
/**
|
|
71
|
+
* Build a Stream that emits a Message for every dispatch of a DOM event,
|
|
72
|
+
* registering the listener when the Stream's scope opens and removing it when
|
|
73
|
+
* the scope closes.
|
|
74
|
+
*
|
|
75
|
+
* The listener lifecycle uses `Effect.acquireRelease`. The `addEventListener`
|
|
76
|
+
* call happens inside the acquire Effect, and the matching
|
|
77
|
+
* `removeEventListener` is registered only after acquire completes, so the
|
|
78
|
+
* listener never leaks on interruption.
|
|
79
|
+
*
|
|
80
|
+
* This is a Stream, not a Subscription entry. Wrap it with
|
|
81
|
+
* `Subscription.persistent` for a listener whose lifetime spans the whole
|
|
82
|
+
* Subscriptions record, or plug it into a `Subscription.make` entry's
|
|
83
|
+
* `dependenciesToStream` (typically behind `Stream.when`) to gate it on a
|
|
84
|
+
* Model condition.
|
|
85
|
+
*
|
|
86
|
+
* For a listener that reacts to only some events, reach for
|
|
87
|
+
* `fromEventFilterMap`, whose mapper returns `Option<Message>`.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const subscriptions = Subscription.make<Model, Message>()(entry => ({
|
|
92
|
+
* shortcut: entry(
|
|
93
|
+
* { isListening: S.Boolean },
|
|
94
|
+
* {
|
|
95
|
+
* modelToDependencies: model => ({ isListening: model.isListening }),
|
|
96
|
+
* dependenciesToStream: ({ isListening }) =>
|
|
97
|
+
* Stream.when(
|
|
98
|
+
* Subscription.fromEvent<KeyboardEvent, Message>({
|
|
99
|
+
* target: window,
|
|
100
|
+
* type: 'keydown',
|
|
101
|
+
* toMessage: event => PressedKey({ key: event.key }),
|
|
102
|
+
* }),
|
|
103
|
+
* Effect.sync(() => isListening),
|
|
104
|
+
* ),
|
|
105
|
+
* },
|
|
106
|
+
* ),
|
|
107
|
+
* }))
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export const fromEvent = (config) => fromEventFilterMap({
|
|
111
|
+
...config,
|
|
112
|
+
toMessage: event => Option.some(config.toMessage(event)),
|
|
113
|
+
});
|
|
@@ -2,6 +2,6 @@ export { aggregate, lift, make, persistent } from '../runtime/subscription.js';
|
|
|
2
2
|
export type { EntryWithoutKeepAlive, Subscription, Subscriptions, } from '../runtime/subscription.js';
|
|
3
3
|
export { animationFrame } from './animationFrame.js';
|
|
4
4
|
export type { AnimationFrameConfig } from './animationFrame.js';
|
|
5
|
-
export { fromEvent } from './fromEvent.js';
|
|
6
|
-
export type { FromEventConfig } from './fromEvent.js';
|
|
5
|
+
export { fromEvent, fromEventFilterMap } from './fromEvent.js';
|
|
6
|
+
export type { FromEventConfig, FromEventFilterMapConfig } from './fromEvent.js';
|
|
7
7
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAE9E,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,aAAa,GACd,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAE9E,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,aAAa,GACd,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAE9D,YAAY,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Schema as S } from 'effect';
|
|
2
|
-
import type { Html } from '../../html/index.js';
|
|
3
|
-
export declare const Model: S.Struct<{}>;
|
|
4
|
-
export type Model = typeof Model.Type;
|
|
2
|
+
import type { Attribute, Html } from '../../html/index.js';
|
|
5
3
|
export declare const IgnoredInteraction: import("../../schema/index.js").CallableTaggedStruct<"IgnoredInteraction", {}>;
|
|
6
4
|
export declare const Message: S.Union<readonly [import("../../schema/index.js").CallableTaggedStruct<"IgnoredInteraction", {}>]>;
|
|
7
5
|
export type Message = typeof Message.Type;
|
|
8
|
-
export
|
|
6
|
+
export type Model = Readonly<{
|
|
7
|
+
attribute: Attribute<Message>;
|
|
8
|
+
}>;
|
|
9
9
|
export declare const update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<never>];
|
|
10
|
-
export declare const
|
|
11
|
-
|
|
10
|
+
export declare const testId = "attribute-host";
|
|
11
|
+
export declare const view: (model: Model) => Html;
|
|
12
|
+
//# sourceMappingURL=attributes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/test/apps/attributes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAM1D,eAAO,MAAM,kBAAkB,gFAA0B,CAAA;AAEzD,eAAO,MAAM,OAAO,oGAAgC,CAAA;AACpD,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC;IAAE,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAAA;AAI/D,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAMrC,CAAA;AAMH,eAAO,MAAM,MAAM,mBAAU,CAAA;AAE7B,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAInC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Match as M, Schema as S } from 'effect';
|
|
2
|
+
import { html } from '../../html/index.js';
|
|
3
|
+
import { m } from '../../message/index.js';
|
|
4
|
+
// MESSAGE
|
|
5
|
+
export const IgnoredInteraction = m('IgnoredInteraction');
|
|
6
|
+
export const Message = S.Union([IgnoredInteraction]);
|
|
7
|
+
// UPDATE
|
|
8
|
+
export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
|
|
9
|
+
IgnoredInteraction: () => [model, []],
|
|
10
|
+
}));
|
|
11
|
+
// VIEW
|
|
12
|
+
const TEST_ID = 'attribute-host';
|
|
13
|
+
export const testId = TEST_ID;
|
|
14
|
+
export const view = (model) => {
|
|
15
|
+
const h = html();
|
|
16
|
+
return h.div([h.DataAttribute('testid', TEST_ID), model.attribute], []);
|
|
17
|
+
};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"svgAttributes.d.ts","sourceRoot":"","sources":["../../../src/test/apps/svgAttributes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAM/C,eAAO,MAAM,KAAK,cAAe,CAAA;AACjC,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,kBAAkB,gFAA0B,CAAA;AAEzD,eAAO,MAAM,OAAO,oGAAgC,CAAA;AACpD,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,YAAY,EAAE,KAAU,CAAA;AAIrC,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAMrC,CAAA;AAIH,eAAO,MAAM,IAAI,GAAI,QAAQ,KAAK,KAAG,IAgHpC,CAAA"}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { Match as M, Schema as S } from 'effect';
|
|
2
|
-
import { html } from '../../html/index.js';
|
|
3
|
-
import { m } from '../../message/index.js';
|
|
4
|
-
// MODEL
|
|
5
|
-
export const Model = S.Struct({});
|
|
6
|
-
// MESSAGE
|
|
7
|
-
export const IgnoredInteraction = m('IgnoredInteraction');
|
|
8
|
-
export const Message = S.Union([IgnoredInteraction]);
|
|
9
|
-
// INIT
|
|
10
|
-
export const initialModel = {};
|
|
11
|
-
// UPDATE
|
|
12
|
-
export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
|
|
13
|
-
IgnoredInteraction: () => [model, []],
|
|
14
|
-
}));
|
|
15
|
-
// VIEW
|
|
16
|
-
export const view = (_model) => {
|
|
17
|
-
const h = html();
|
|
18
|
-
return h.svg([
|
|
19
|
-
h.DataAttribute('testid', 'svg-root'),
|
|
20
|
-
h.ViewBox('0 0 100 100'),
|
|
21
|
-
h.PreserveAspectRatio('xMidYMid meet'),
|
|
22
|
-
h.Color('black'),
|
|
23
|
-
h.Overflow('visible'),
|
|
24
|
-
], [
|
|
25
|
-
h.text([
|
|
26
|
-
h.DataAttribute('testid', 'svg-text'),
|
|
27
|
-
h.Dx('1'),
|
|
28
|
-
h.Dy('2'),
|
|
29
|
-
h.Rotate('15'),
|
|
30
|
-
h.TextAnchor('middle'),
|
|
31
|
-
h.DominantBaseline('central'),
|
|
32
|
-
h.AlignmentBaseline('middle'),
|
|
33
|
-
h.BaselineShift('super'),
|
|
34
|
-
h.TextLength('40'),
|
|
35
|
-
h.LengthAdjust('spacing'),
|
|
36
|
-
h.FontFamily('serif'),
|
|
37
|
-
h.FontSize('12'),
|
|
38
|
-
h.FontWeight('bold'),
|
|
39
|
-
h.FontStyle('italic'),
|
|
40
|
-
h.LetterSpacing('1'),
|
|
41
|
-
h.WordSpacing('2'),
|
|
42
|
-
h.TextDecoration('underline'),
|
|
43
|
-
h.WritingMode('horizontal-tb'),
|
|
44
|
-
h.Visibility('visible'),
|
|
45
|
-
h.Display('inline'),
|
|
46
|
-
h.Cursor('pointer'),
|
|
47
|
-
h.PointerEvents('none'),
|
|
48
|
-
], ['Label']),
|
|
49
|
-
h.rect([
|
|
50
|
-
h.DataAttribute('testid', 'svg-rect'),
|
|
51
|
-
h.Rx('4'),
|
|
52
|
-
h.Ry('4'),
|
|
53
|
-
h.PathLength('100'),
|
|
54
|
-
h.FillOpacity('0.5'),
|
|
55
|
-
h.StrokeOpacity('0.8'),
|
|
56
|
-
h.StrokeMiterlimit('2'),
|
|
57
|
-
h.PaintOrder('stroke'),
|
|
58
|
-
h.VectorEffect('non-scaling-stroke'),
|
|
59
|
-
h.ShapeRendering('crispEdges'),
|
|
60
|
-
h.TextRendering('optimizeLegibility'),
|
|
61
|
-
h.ImageRendering('pixelated'),
|
|
62
|
-
h.ClipPath('url(#clip)'),
|
|
63
|
-
h.Mask('url(#mask)'),
|
|
64
|
-
h.Filter('url(#filter)'),
|
|
65
|
-
h.MarkerStart('url(#start)'),
|
|
66
|
-
h.MarkerMid('url(#mid)'),
|
|
67
|
-
h.MarkerEnd('url(#end)'),
|
|
68
|
-
], []),
|
|
69
|
-
h.linearGradient([
|
|
70
|
-
h.DataAttribute('testid', 'svg-gradient'),
|
|
71
|
-
h.GradientUnits('userSpaceOnUse'),
|
|
72
|
-
h.GradientTransform('rotate(45)'),
|
|
73
|
-
h.SpreadMethod('reflect'),
|
|
74
|
-
h.Fx('0.1'),
|
|
75
|
-
h.Fy('0.2'),
|
|
76
|
-
h.Fr('0.3'),
|
|
77
|
-
h.ClipPathUnits('userSpaceOnUse'),
|
|
78
|
-
h.MaskUnits('userSpaceOnUse'),
|
|
79
|
-
h.MaskContentUnits('userSpaceOnUse'),
|
|
80
|
-
h.FilterUnits('userSpaceOnUse'),
|
|
81
|
-
h.PrimitiveUnits('userSpaceOnUse'),
|
|
82
|
-
], [
|
|
83
|
-
h.stop([
|
|
84
|
-
h.DataAttribute('testid', 'svg-stop'),
|
|
85
|
-
h.Offset('0.5'),
|
|
86
|
-
h.StopColor('red'),
|
|
87
|
-
h.StopOpacity('0.9'),
|
|
88
|
-
], []),
|
|
89
|
-
]),
|
|
90
|
-
h.pattern([
|
|
91
|
-
h.DataAttribute('testid', 'svg-pattern'),
|
|
92
|
-
h.PatternUnits('userSpaceOnUse'),
|
|
93
|
-
h.PatternContentUnits('userSpaceOnUse'),
|
|
94
|
-
h.PatternTransform('scale(2)'),
|
|
95
|
-
], []),
|
|
96
|
-
h.marker([
|
|
97
|
-
h.DataAttribute('testid', 'svg-marker'),
|
|
98
|
-
h.MarkerWidth('6'),
|
|
99
|
-
h.MarkerHeight('6'),
|
|
100
|
-
h.MarkerUnits('strokeWidth'),
|
|
101
|
-
h.RefX('3'),
|
|
102
|
-
h.RefY('3'),
|
|
103
|
-
h.Orient('auto'),
|
|
104
|
-
], []),
|
|
105
|
-
]);
|
|
106
|
-
};
|