foldkit 0.30.0 → 0.32.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/README.md CHANGED
@@ -153,17 +153,17 @@ This is what makes Foldkit unusually AI-friendly. The same property that makes t
153
153
  ## Examples
154
154
 
155
155
  - **[Counter](https://github.com/devinjameson/foldkit/blob/main/examples/counter/src/main.ts)** — Increment/decrement with reset
156
- - **[Stopwatch](https://github.com/devinjameson/foldkit/blob/main/examples/stopwatch/src/main.ts)** — Timer with start/stop/reset
157
- - **[Weather](https://github.com/devinjameson/foldkit/blob/main/examples/weather/src/main.ts)** — HTTP requests with async state handling
158
156
  - **[Todo](https://github.com/devinjameson/foldkit/blob/main/examples/todo/src/main.ts)** — CRUD operations with localStorage persistence
157
+ - **[Stopwatch](https://github.com/devinjameson/foldkit/blob/main/examples/stopwatch/src/main.ts)** — Timer with start/stop/reset
158
+ - **[Error View](https://github.com/devinjameson/foldkit/blob/main/examples/error-view/src/main.ts)** — Custom error fallback UI
159
159
  - **[Form](https://github.com/devinjameson/foldkit/blob/main/examples/form/src/main.ts)** — Form validation with async email checking
160
+ - **[Weather](https://github.com/devinjameson/foldkit/blob/main/examples/weather/src/main.ts)** — HTTP requests with async state handling
160
161
  - **[Routing](https://github.com/devinjameson/foldkit/blob/main/examples/routing/src/main.ts)** — URL routing with parser combinators
161
- - **[Shopping Cart](https://github.com/devinjameson/foldkit/blob/main/examples/shopping-cart/src/main.ts)** — Nested models and complex state
162
+ - **[Query Sync](https://github.com/devinjameson/foldkit/blob/main/examples/query-sync/src/main.ts)** — URL query parameter sync with filtering and sorting
162
163
  - **[Snake](https://github.com/devinjameson/foldkit/blob/main/examples/snake/src/main.ts)** — Classic game built with Subscriptions
163
- - **[WebSocket Chat](https://github.com/devinjameson/foldkit/blob/main/examples/websocket-chat/src/main.ts)** — Managed Resources with WebSocket integration
164
164
  - **[Auth](https://github.com/devinjameson/foldkit/blob/main/examples/auth/src/main.ts)** — Authentication flow with Submodels and OutMessage
165
- - **[Query Sync](https://github.com/devinjameson/foldkit/blob/main/examples/query-sync/src/main.ts)** — URL query parameter sync with filtering and sorting
166
- - **[Error View](https://github.com/devinjameson/foldkit/blob/main/examples/error-view/src/main.ts)** — Custom error fallback UI
165
+ - **[Shopping Cart](https://github.com/devinjameson/foldkit/blob/main/examples/shopping-cart/src/main.ts)** — Nested models and complex state
166
+ - **[WebSocket Chat](https://github.com/devinjameson/foldkit/blob/main/examples/websocket-chat/src/main.ts)** — Managed Resources with WebSocket integration
167
167
  - **[Typing Game](https://github.com/devinjameson/foldkit/tree/main/packages/typing-game)** — Multiplayer typing game with Effect RPC backend
168
168
 
169
169
  ## License
@@ -1,10 +1,12 @@
1
1
  import { Schema as S } from 'effect';
2
2
  import type { Command } from '../../command';
3
3
  import type { Html } from '../../html';
4
- /** Schema for the dialog component's state, tracking its unique ID and open/closed status. */
4
+ /** Schema for the dialog component's state, tracking its unique ID, open/closed status, animation support, and transition phase. */
5
5
  export declare const Model: S.Struct<{
6
6
  id: typeof S.String;
7
7
  isOpen: typeof S.Boolean;
8
+ isAnimated: typeof S.Boolean;
9
+ transitionState: S.Literal<["Idle", "EnterStart", "EnterAnimating", "LeaveStart", "LeaveAnimating"]>;
8
10
  }>;
9
11
  export type Model = typeof Model.Type;
10
12
  /** Sent when the dialog should open. Triggers the showModal command. */
@@ -13,21 +15,27 @@ export declare const Opened: import("../../schema").CallableTaggedStruct<"Opened
13
15
  export declare const Closed: import("../../schema").CallableTaggedStruct<"Closed", {}>;
14
16
  /** Placeholder message used when no action is needed, such as after a showModal or closeModal command completes. */
15
17
  export declare const NoOp: import("../../schema").CallableTaggedStruct<"NoOp", {}>;
18
+ /** Sent internally when a double-rAF completes, advancing the transition to its animating phase. */
19
+ export declare const AdvancedTransitionFrame: import("../../schema").CallableTaggedStruct<"AdvancedTransitionFrame", {}>;
20
+ /** Sent internally when all CSS transitions on the dialog panel have completed. */
21
+ export declare const EndedTransition: import("../../schema").CallableTaggedStruct<"EndedTransition", {}>;
16
22
  /** Union of all messages the dialog component can produce. */
17
- export declare const Message: S.Union<[import("../../schema").CallableTaggedStruct<"Opened", {}>, import("../../schema").CallableTaggedStruct<"Closed", {}>, import("../../schema").CallableTaggedStruct<"NoOp", {}>]>;
23
+ export declare const Message: S.Union<[import("../../schema").CallableTaggedStruct<"Opened", {}>, import("../../schema").CallableTaggedStruct<"Closed", {}>, import("../../schema").CallableTaggedStruct<"NoOp", {}>, import("../../schema").CallableTaggedStruct<"AdvancedTransitionFrame", {}>, import("../../schema").CallableTaggedStruct<"EndedTransition", {}>]>;
18
24
  export type Opened = typeof Opened.Type;
19
25
  export type Closed = typeof Closed.Type;
20
26
  export type NoOp = typeof NoOp.Type;
21
27
  export type Message = typeof Message.Type;
22
- /** Configuration for creating a dialog model with `init`. */
28
+ /** Configuration for creating a dialog model with `init`. `isAnimated` enables CSS transition coordination (default `false`). */
23
29
  export type InitConfig = Readonly<{
24
30
  id: string;
25
31
  isOpen?: boolean;
32
+ isAnimated?: boolean;
26
33
  }>;
27
- /** Creates an initial dialog model from a config. Defaults to closed. */
34
+ /** Creates an initial dialog model from a config. Defaults to closed and non-animated. */
28
35
  export declare const init: (config: InitConfig) => Model;
36
+ type UpdateReturn = [Model, ReadonlyArray<Command<Message>>];
29
37
  /** Processes a dialog message and returns the next model and commands. */
30
- export declare const update: (model: Model, message: Message) => [Model, ReadonlyArray<Command<Message>>];
38
+ export declare const update: (model: Model, message: Message) => UpdateReturn;
31
39
  /** Returns the ID used for `aria-labelledby` on the dialog. Apply this to your title element. */
32
40
  export declare const titleId: (model: Model) => string;
33
41
  /** Returns the ID used for `aria-describedby` on the dialog. Apply this to your description element. */
@@ -46,4 +54,5 @@ export declare const view: <Message>(config: ViewConfig<Message>) => Html;
46
54
  /** Creates a memoized dialog view. Static config is captured in a closure;
47
55
  * only `model` and `toMessage` are compared per render via `createLazy`. */
48
56
  export declare const lazy: <Message>(staticConfig: Omit<ViewConfig<Message>, "model" | "toMessage">) => ((model: Model, toMessage: ViewConfig<Message>["toMessage"]) => Html);
57
+ export {};
49
58
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAQtC,8FAA8F;AAC9F,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AAEF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,wEAAwE;AACxE,eAAO,MAAM,MAAM,2DAAc,CAAA;AACjC,wHAAwH;AACxH,eAAO,MAAM,MAAM,2DAAc,CAAA;AACjC,oHAAoH;AACpH,eAAO,MAAM,IAAI,yDAAY,CAAA;AAE7B,8DAA8D;AAC9D,eAAO,MAAM,OAAO,0LAAgC,CAAA;AAEpD,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AACvC,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AACvC,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAA;AAEnC,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAC,CAAA;AAEF,yEAAyE;AACzE,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KAGxC,CAAA;AAMF,0EAA0E;AAC1E,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CA4BvC,CAAA;AAIH,iGAAiG;AACjG,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,MAA6B,CAAA;AAEpE,wGAAwG;AACxG,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,MAAmC,CAAA;AAEhF,wDAAwD;AACxD,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAA;IAC9C,YAAY,EAAE,IAAI,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAC,CAAA;AAEF,sGAAsG;AACtG,eAAO,MAAM,IAAI,GAAI,OAAO,EAAE,QAAQ,UAAU,CAAC,OAAO,CAAC,KAAG,IA6C3D,CAAA;AAED;6EAC6E;AAC7E,eAAO,MAAM,IAAI,GAAI,OAAO,EAC1B,cAAc,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC,KAC7D,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,KAAK,IAAI,CAgBtE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqC,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAG5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAStC,oIAAoI;AACpI,eAAO,MAAM,KAAK;;;;;EAKhB,CAAA;AAEF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,wEAAwE;AACxE,eAAO,MAAM,MAAM,2DAAc,CAAA;AACjC,wHAAwH;AACxH,eAAO,MAAM,MAAM,2DAAc,CAAA;AACjC,oHAAoH;AACpH,eAAO,MAAM,IAAI,yDAAY,CAAA;AAC7B,oGAAoG;AACpG,eAAO,MAAM,uBAAuB,4EAA+B,CAAA;AACnE,mFAAmF;AACnF,eAAO,MAAM,eAAe,oEAAuB,CAAA;AAEnD,8DAA8D;AAC9D,eAAO,MAAM,OAAO,0UAMnB,CAAA;AAED,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AACvC,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AACvC,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAA;AAEnC,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,iIAAiI;AACjI,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAC,CAAA;AAEF,0FAA0F;AAC1F,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KAKxC,CAAA;AAOF,KAAK,YAAY,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AAG5D,0EAA0E;AAC1E,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,EAAE,SAAS,OAAO,KAAG,YAwGvD,CAAA;AAID,iGAAiG;AACjG,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,MAA6B,CAAA;AAEpE,wGAAwG;AACxG,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,MAAmC,CAAA;AAEhF,wDAAwD;AACxD,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAA;IAC9C,YAAY,EAAE,IAAI,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAC,CAAA;AAEF,sGAAsG;AACtG,eAAO,MAAM,IAAI,GAAI,OAAO,EAAE,QAAQ,UAAU,CAAC,OAAO,CAAC,KAAG,IAqF3D,CAAA;AAED;6EAC6E;AAC7E,eAAO,MAAM,IAAI,GAAI,OAAO,EAC1B,cAAc,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC,KAC7D,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,KAAK,IAAI,CAgBtE,CAAA"}
@@ -1,14 +1,18 @@
1
- import { Effect, Match as M, Option, Schema as S } from 'effect';
1
+ import { Array, Effect, Match as M, Option, Schema as S } from 'effect';
2
+ import { OptionExt } from '../../effectExtensions';
2
3
  import { html } from '../../html';
3
4
  import { createLazy } from '../../html/lazy';
4
5
  import { m } from '../../message';
5
6
  import { evo } from '../../struct';
6
7
  import * as Task from '../../task';
8
+ import { TransitionState } from '../transition';
7
9
  // MODEL
8
- /** Schema for the dialog component's state, tracking its unique ID and open/closed status. */
10
+ /** Schema for the dialog component's state, tracking its unique ID, open/closed status, animation support, and transition phase. */
9
11
  export const Model = S.Struct({
10
12
  id: S.String,
11
13
  isOpen: S.Boolean,
14
+ isAnimated: S.Boolean,
15
+ transitionState: TransitionState,
12
16
  });
13
17
  // MESSAGE
14
18
  /** Sent when the dialog should open. Triggers the showModal command. */
@@ -17,27 +21,78 @@ export const Opened = m('Opened');
17
21
  export const Closed = m('Closed');
18
22
  /** Placeholder message used when no action is needed, such as after a showModal or closeModal command completes. */
19
23
  export const NoOp = m('NoOp');
24
+ /** Sent internally when a double-rAF completes, advancing the transition to its animating phase. */
25
+ export const AdvancedTransitionFrame = m('AdvancedTransitionFrame');
26
+ /** Sent internally when all CSS transitions on the dialog panel have completed. */
27
+ export const EndedTransition = m('EndedTransition');
20
28
  /** Union of all messages the dialog component can produce. */
21
- export const Message = S.Union(Opened, Closed, NoOp);
22
- /** Creates an initial dialog model from a config. Defaults to closed. */
29
+ export const Message = S.Union(Opened, Closed, NoOp, AdvancedTransitionFrame, EndedTransition);
30
+ /** Creates an initial dialog model from a config. Defaults to closed and non-animated. */
23
31
  export const init = (config) => ({
24
32
  id: config.id,
25
33
  isOpen: config.isOpen ?? false,
34
+ isAnimated: config.isAnimated ?? false,
35
+ transitionState: 'Idle',
26
36
  });
27
37
  // UPDATE
28
38
  const dialogSelector = (id) => `#${id}`;
39
+ const panelSelector = (id) => `#${id}-panel`;
40
+ const withUpdateReturn = M.withReturnType();
29
41
  /** Processes a dialog message and returns the next model and commands. */
30
- export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
31
- Opened: () => {
32
- const maybeShow = Option.liftPredicate(Task.showModal(dialogSelector(model.id)).pipe(Effect.ignore, Effect.as(NoOp())), () => !model.isOpen);
33
- return [evo(model, { isOpen: () => true }), Option.toArray(maybeShow)];
34
- },
35
- Closed: () => {
36
- const maybeClose = Option.liftPredicate(Task.closeModal(dialogSelector(model.id)).pipe(Effect.ignore, Effect.as(NoOp())), () => model.isOpen);
37
- return [evo(model, { isOpen: () => false }), Option.toArray(maybeClose)];
38
- },
39
- NoOp: () => [model, []],
40
- }));
42
+ export const update = (model, message) => {
43
+ const maybeNextFrame = OptionExt.when(model.isAnimated, Task.nextFrame.pipe(Effect.as(AdvancedTransitionFrame())));
44
+ return M.value(message).pipe(withUpdateReturn, M.tagsExhaustive({
45
+ Opened: () => {
46
+ const maybeShow = Option.liftPredicate(Task.lockScroll.pipe(Effect.andThen(() => Task.showModal(dialogSelector(model.id))), Effect.ignore, Effect.as(NoOp())), () => !model.isOpen);
47
+ return [
48
+ evo(model, {
49
+ isOpen: () => true,
50
+ transitionState: () => (model.isAnimated ? 'EnterStart' : 'Idle'),
51
+ }),
52
+ Array.getSomes([maybeShow, maybeNextFrame]),
53
+ ];
54
+ },
55
+ Closed: () => {
56
+ const isLeaving = model.transitionState === 'LeaveStart' ||
57
+ model.transitionState === 'LeaveAnimating';
58
+ if (isLeaving) {
59
+ return [model, []];
60
+ }
61
+ if (model.isAnimated) {
62
+ return [
63
+ evo(model, {
64
+ isOpen: () => false,
65
+ transitionState: () => 'LeaveStart',
66
+ }),
67
+ Option.toArray(maybeNextFrame),
68
+ ];
69
+ }
70
+ const maybeClose = Option.liftPredicate(Task.closeModal(dialogSelector(model.id)).pipe(Effect.andThen(() => Task.unlockScroll), Effect.ignore, Effect.as(NoOp())), () => model.isOpen);
71
+ return [evo(model, { isOpen: () => false }), Option.toArray(maybeClose)];
72
+ },
73
+ AdvancedTransitionFrame: () => M.value(model.transitionState).pipe(withUpdateReturn, M.when('EnterStart', () => [
74
+ evo(model, { transitionState: () => 'EnterAnimating' }),
75
+ [
76
+ Task.waitForTransitions(panelSelector(model.id)).pipe(Effect.as(EndedTransition())),
77
+ ],
78
+ ]), M.when('LeaveStart', () => [
79
+ evo(model, { transitionState: () => 'LeaveAnimating' }),
80
+ [
81
+ Task.waitForTransitions(panelSelector(model.id)).pipe(Effect.as(EndedTransition())),
82
+ ],
83
+ ]), M.orElse(() => [model, []])),
84
+ EndedTransition: () => M.value(model.transitionState).pipe(withUpdateReturn, M.when('EnterAnimating', () => [
85
+ evo(model, { transitionState: () => 'Idle' }),
86
+ [],
87
+ ]), M.when('LeaveAnimating', () => [
88
+ evo(model, { transitionState: () => 'Idle' }),
89
+ [
90
+ Task.closeModal(dialogSelector(model.id)).pipe(Effect.andThen(() => Task.unlockScroll), Effect.ignore, Effect.as(NoOp())),
91
+ ],
92
+ ]), M.orElse(() => [model, []])),
93
+ NoOp: () => [model, []],
94
+ }));
95
+ };
41
96
  // VIEW
42
97
  /** Returns the ID used for `aria-labelledby` on the dialog. Apply this to your title element. */
43
98
  export const titleId = (model) => `${model.id}-title`;
@@ -45,19 +100,48 @@ export const titleId = (model) => `${model.id}-title`;
45
100
  export const descriptionId = (model) => `${model.id}-description`;
46
101
  /** Renders a headless dialog component backed by the native `<dialog>` element with `showModal()`. */
47
102
  export const view = (config) => {
48
- const { AriaDescribedBy, AriaLabelledBy, Class, DataAttribute, Id, OnCancel, OnClick, keyed, } = html();
49
- const { model: { id, isOpen }, toMessage, panelContent, panelClassName, backdropClassName, className, } = config;
103
+ const { AriaDescribedBy, AriaLabelledBy, Class, DataAttribute, Id, OnCancel, OnClick, Style, keyed, } = html();
104
+ const { model: { id, isOpen, transitionState }, toMessage, panelContent, panelClassName, backdropClassName, className, } = config;
105
+ const isLeaving = transitionState === 'LeaveStart' || transitionState === 'LeaveAnimating';
106
+ const isVisible = isOpen || isLeaving;
107
+ const transitionAttributes = M.value(transitionState).pipe(M.when('EnterStart', () => [
108
+ DataAttribute('closed', ''),
109
+ DataAttribute('enter', ''),
110
+ DataAttribute('transition', ''),
111
+ ]), M.when('EnterAnimating', () => [
112
+ DataAttribute('enter', ''),
113
+ DataAttribute('transition', ''),
114
+ ]), M.when('LeaveStart', () => [
115
+ DataAttribute('leave', ''),
116
+ DataAttribute('transition', ''),
117
+ ]), M.when('LeaveAnimating', () => [
118
+ DataAttribute('closed', ''),
119
+ DataAttribute('leave', ''),
120
+ DataAttribute('transition', ''),
121
+ ]), M.orElse(() => []));
50
122
  const dialogAttributes = [
51
123
  Id(id),
52
124
  AriaLabelledBy(`${id}-title`),
53
125
  AriaDescribedBy(`${id}-description`),
54
126
  OnCancel(toMessage(Closed())),
55
- ...(isOpen ? [DataAttribute('open', '')] : []),
127
+ Style({
128
+ overflow: 'hidden',
129
+ maxWidth: '100%',
130
+ maxHeight: '100%',
131
+ padding: '0',
132
+ border: 'none',
133
+ background: 'transparent',
134
+ }),
135
+ ...(isVisible ? [DataAttribute('open', '')] : []),
56
136
  ...(className ? [Class(className)] : []),
57
137
  ];
58
- const backdrop = keyed('div')(`${id}-backdrop`, [Class(backdropClassName), OnClick(toMessage(Closed()))], []);
59
- const panel = keyed('div')(`${id}-panel`, [Class(panelClassName)], [panelContent]);
60
- const content = isOpen ? [backdrop, panel] : [];
138
+ const backdrop = keyed('div')(`${id}-backdrop`, [
139
+ Class(backdropClassName),
140
+ ...transitionAttributes,
141
+ ...(isLeaving ? [] : [OnClick(toMessage(Closed()))]),
142
+ ], []);
143
+ const panel = keyed('div')(`${id}-panel`, [Id(`${id}-panel`), Class(panelClassName), ...transitionAttributes], [panelContent]);
144
+ const content = isVisible ? [backdrop, panel] : [];
61
145
  return keyed('dialog')(id, dialogAttributes, content);
62
146
  };
63
147
  /** Creates a memoized dialog view. Static config is captured in a closure;
@@ -1,3 +1,3 @@
1
- export { init, update, view, lazy, titleId, descriptionId, Model, Message, Opened, Closed, NoOp, } from './index';
1
+ export { init, update, view, lazy, titleId, descriptionId, Model, Message, Opened, Closed, NoOp, AdvancedTransitionFrame, EndedTransition, } from './index';
2
2
  export type { InitConfig, ViewConfig } from './index';
3
3
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/dialog/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,aAAa,EACb,KAAK,EACL,OAAO,EACP,MAAM,EACN,MAAM,EACN,IAAI,GACL,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/dialog/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,aAAa,EACb,KAAK,EACL,OAAO,EACP,MAAM,EACN,MAAM,EACN,IAAI,EACJ,uBAAuB,EACvB,eAAe,GAChB,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA"}
@@ -1 +1 @@
1
- export { init, update, view, lazy, titleId, descriptionId, Model, Message, Opened, Closed, NoOp, } from './index';
1
+ export { init, update, view, lazy, titleId, descriptionId, Model, Message, Opened, Closed, NoOp, AdvancedTransitionFrame, EndedTransition, } from './index';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foldkit",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "description": "Elm-inspired UI framework powered by Effect",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",