foldkit 0.36.1 → 0.36.2
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/task/dom.d.ts
CHANGED
|
@@ -19,12 +19,19 @@ export declare const focus: (selector: string) => Effect.Effect<void, ElementNot
|
|
|
19
19
|
* Uses requestAnimationFrame to ensure the DOM is updated before attempting to show.
|
|
20
20
|
* Fails with `ElementNotFound` if the selector does not match an `HTMLDialogElement`.
|
|
21
21
|
*
|
|
22
|
+
* Pass `focusSelector` to focus an element inside the dialog in the same frame
|
|
23
|
+
* as `show()` — required on mobile browsers where `focus()` is ignored outside
|
|
24
|
+
* the original user-gesture call stack.
|
|
25
|
+
*
|
|
22
26
|
* @example
|
|
23
27
|
* ```typescript
|
|
24
28
|
* Task.showModal('#my-dialog').pipe(Effect.ignore, Effect.as(CompletedDialogShow()))
|
|
29
|
+
* Task.showModal('#my-dialog', { focusSelector: '#search-input' }).pipe(Effect.ignore, Effect.as(CompletedDialogShow()))
|
|
25
30
|
* ```
|
|
26
31
|
*/
|
|
27
|
-
export declare const showModal: (selector: string
|
|
32
|
+
export declare const showModal: (selector: string, options?: Readonly<{
|
|
33
|
+
focusSelector?: string;
|
|
34
|
+
}>) => Effect.Effect<void, ElementNotFound>;
|
|
28
35
|
/**
|
|
29
36
|
* Closes a dialog element using `.close()`.
|
|
30
37
|
* Cleans up the keyboard handlers installed by `showModal`.
|
package/dist/task/dom.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/task/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAMP,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAmBzC;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAWxE,CAAA;AAEJ
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/task/dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,MAAM,EAMP,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAmBzC;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAWxE,CAAA;AAEJ;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,SAAS,GACpB,UAAU,MAAM,EAChB,UAAU,QAAQ,CAAC;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,KAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAiDlC,CAAA;AAuBJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,GACrB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAiBlC,CAAA;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAWlC,CAAA;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,KACf,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAYlC,CAAA;AAEJ,0EAA0E;AAC1E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,CAAA;AAEhD;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GACvB,UAAU,MAAM,EAChB,WAAW,cAAc,KACxB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAuClC,CAAA"}
|
package/dist/task/dom.js
CHANGED
|
@@ -41,12 +41,17 @@ export const focus = (selector) => Effect.async(resume => {
|
|
|
41
41
|
* Uses requestAnimationFrame to ensure the DOM is updated before attempting to show.
|
|
42
42
|
* Fails with `ElementNotFound` if the selector does not match an `HTMLDialogElement`.
|
|
43
43
|
*
|
|
44
|
+
* Pass `focusSelector` to focus an element inside the dialog in the same frame
|
|
45
|
+
* as `show()` — required on mobile browsers where `focus()` is ignored outside
|
|
46
|
+
* the original user-gesture call stack.
|
|
47
|
+
*
|
|
44
48
|
* @example
|
|
45
49
|
* ```typescript
|
|
46
50
|
* Task.showModal('#my-dialog').pipe(Effect.ignore, Effect.as(CompletedDialogShow()))
|
|
51
|
+
* Task.showModal('#my-dialog', { focusSelector: '#search-input' }).pipe(Effect.ignore, Effect.as(CompletedDialogShow()))
|
|
47
52
|
* ```
|
|
48
53
|
*/
|
|
49
|
-
export const showModal = (selector) => Effect.async(resume => {
|
|
54
|
+
export const showModal = (selector, options) => Effect.async(resume => {
|
|
50
55
|
requestAnimationFrame(() => {
|
|
51
56
|
const element = document.querySelector(selector);
|
|
52
57
|
if (element instanceof HTMLDialogElement) {
|
|
@@ -71,6 +76,12 @@ export const showModal = (selector) => Effect.async(resume => {
|
|
|
71
76
|
};
|
|
72
77
|
document.addEventListener('keydown', handleKeydown);
|
|
73
78
|
dialogCleanups.set(element, () => document.removeEventListener('keydown', handleKeydown));
|
|
79
|
+
if (options?.focusSelector) {
|
|
80
|
+
const focusTarget = element.querySelector(options.focusSelector);
|
|
81
|
+
if (focusTarget instanceof HTMLElement) {
|
|
82
|
+
focusTarget.focus();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
74
85
|
resume(Effect.void);
|
|
75
86
|
}
|
|
76
87
|
else {
|
|
@@ -7,6 +7,7 @@ export declare const Model: S.Struct<{
|
|
|
7
7
|
isOpen: typeof S.Boolean;
|
|
8
8
|
isAnimated: typeof S.Boolean;
|
|
9
9
|
transitionState: S.Literal<["Idle", "EnterStart", "EnterAnimating", "LeaveStart", "LeaveAnimating"]>;
|
|
10
|
+
maybeFocusSelector: S.OptionFromSelf<typeof S.String>;
|
|
10
11
|
}>;
|
|
11
12
|
export type Model = typeof Model.Type;
|
|
12
13
|
/** Sent when the dialog should open. Triggers the showModal command. */
|
|
@@ -40,6 +41,7 @@ export type InitConfig = Readonly<{
|
|
|
40
41
|
id: string;
|
|
41
42
|
isOpen?: boolean;
|
|
42
43
|
isAnimated?: boolean;
|
|
44
|
+
focusSelector?: string;
|
|
43
45
|
}>;
|
|
44
46
|
/** Creates an initial dialog model from a config. Defaults to closed and non-animated. */
|
|
45
47
|
export declare const init: (config: InitConfig) => Model;
|
|
@@ -1 +1 @@
|
|
|
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;AAE5C,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,IAAI,EAAoB,MAAM,YAAY,CAAA;AAQxE,oIAAoI;AACpI,eAAO,MAAM,KAAK
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/dialog/index.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/dialog/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export const Model = S.Struct({
|
|
|
12
12
|
isOpen: S.Boolean,
|
|
13
13
|
isAnimated: S.Boolean,
|
|
14
14
|
transitionState: TransitionState,
|
|
15
|
+
maybeFocusSelector: S.OptionFromSelf(S.String),
|
|
15
16
|
});
|
|
16
17
|
// MESSAGE
|
|
17
18
|
/** Sent when the dialog should open. Triggers the showModal command. */
|
|
@@ -34,6 +35,7 @@ export const init = (config) => ({
|
|
|
34
35
|
isOpen: config.isOpen ?? false,
|
|
35
36
|
isAnimated: config.isAnimated ?? false,
|
|
36
37
|
transitionState: 'Idle',
|
|
38
|
+
maybeFocusSelector: Option.fromNullable(config.focusSelector),
|
|
37
39
|
});
|
|
38
40
|
// UPDATE
|
|
39
41
|
const dialogSelector = (id) => `#${id}`;
|
|
@@ -44,7 +46,11 @@ export const update = (model, message) => {
|
|
|
44
46
|
const maybeNextFrame = OptionExt.when(model.isAnimated, Task.nextFrame.pipe(Effect.as(AdvancedTransitionFrame())));
|
|
45
47
|
return M.value(message).pipe(withUpdateReturn, M.tagsExhaustive({
|
|
46
48
|
Opened: () => {
|
|
47
|
-
const
|
|
49
|
+
const focusOptions = Option.match(model.maybeFocusSelector, {
|
|
50
|
+
onNone: () => undefined,
|
|
51
|
+
onSome: focusSelector => ({ focusSelector }),
|
|
52
|
+
});
|
|
53
|
+
const maybeShow = Option.liftPredicate(Task.lockScroll.pipe(Effect.andThen(() => Task.showModal(dialogSelector(model.id), focusOptions)), Effect.ignore, Effect.as(CompletedDialogShow())), () => !model.isOpen);
|
|
48
54
|
return [
|
|
49
55
|
evo(model, {
|
|
50
56
|
isOpen: () => true,
|