foldkit 0.33.6 → 0.34.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 +19 -19
- package/dist/devtools/overlay.d.ts.map +1 -1
- package/dist/devtools/overlay.js +76 -33
- package/dist/html/index.d.ts +431 -5
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +1 -0
- package/dist/html/public.d.ts +1 -2
- package/dist/html/public.d.ts.map +1 -1
- package/dist/html/public.js +1 -2
- package/dist/task/dom.d.ts +6 -6
- package/dist/task/dom.js +6 -6
- package/dist/task/inert.d.ts +2 -2
- package/dist/task/inert.js +2 -2
- package/dist/task/scrollLock.d.ts +2 -2
- package/dist/task/scrollLock.js +2 -2
- package/dist/ui/checkbox/index.d.ts +5 -9
- package/dist/ui/checkbox/index.d.ts.map +1 -1
- package/dist/ui/checkbox/index.js +7 -10
- package/dist/ui/checkbox/public.d.ts +1 -1
- package/dist/ui/checkbox/public.d.ts.map +1 -1
- package/dist/ui/combobox/multi.d.ts +31 -10
- package/dist/ui/combobox/multi.d.ts.map +1 -1
- package/dist/ui/combobox/multi.js +1 -1
- package/dist/ui/combobox/public.d.ts +1 -1
- package/dist/ui/combobox/public.d.ts.map +1 -1
- package/dist/ui/combobox/shared.d.ts +53 -14
- package/dist/ui/combobox/shared.d.ts.map +1 -1
- package/dist/ui/combobox/shared.js +72 -28
- package/dist/ui/combobox/single.d.ts +31 -10
- package/dist/ui/combobox/single.d.ts.map +1 -1
- package/dist/ui/combobox/single.js +1 -1
- package/dist/ui/dialog/index.d.ts +14 -8
- package/dist/ui/dialog/index.d.ts.map +1 -1
- package/dist/ui/dialog/index.js +21 -12
- package/dist/ui/dialog/public.d.ts +1 -1
- package/dist/ui/dialog/public.d.ts.map +1 -1
- package/dist/ui/dialog/public.js +1 -1
- package/dist/ui/disclosure/index.d.ts +11 -8
- package/dist/ui/disclosure/index.d.ts.map +1 -1
- package/dist/ui/disclosure/index.js +20 -18
- package/dist/ui/disclosure/public.d.ts +1 -1
- package/dist/ui/disclosure/public.d.ts.map +1 -1
- package/dist/ui/listbox/multi.d.ts +34 -9
- package/dist/ui/listbox/multi.d.ts.map +1 -1
- package/dist/ui/listbox/multi.js +1 -1
- package/dist/ui/listbox/public.d.ts +1 -1
- package/dist/ui/listbox/public.d.ts.map +1 -1
- package/dist/ui/listbox/shared.d.ts +57 -13
- package/dist/ui/listbox/shared.d.ts.map +1 -1
- package/dist/ui/listbox/shared.js +77 -27
- package/dist/ui/listbox/single.d.ts +34 -9
- package/dist/ui/listbox/single.d.ts.map +1 -1
- package/dist/ui/listbox/single.js +1 -1
- package/dist/ui/menu/index.d.ts +39 -11
- package/dist/ui/menu/index.d.ts.map +1 -1
- package/dist/ui/menu/index.js +81 -32
- package/dist/ui/menu/public.d.ts +1 -1
- package/dist/ui/menu/public.d.ts.map +1 -1
- package/dist/ui/popover/index.d.ts +28 -12
- package/dist/ui/popover/index.d.ts.map +1 -1
- package/dist/ui/popover/index.js +50 -24
- package/dist/ui/popover/public.d.ts +1 -1
- package/dist/ui/popover/public.d.ts.map +1 -1
- package/dist/ui/radioGroup/index.d.ts +8 -7
- package/dist/ui/radioGroup/index.d.ts.map +1 -1
- package/dist/ui/radioGroup/index.js +12 -9
- package/dist/ui/radioGroup/public.d.ts +1 -1
- package/dist/ui/radioGroup/public.d.ts.map +1 -1
- package/dist/ui/switch/index.d.ts +5 -9
- package/dist/ui/switch/index.d.ts.map +1 -1
- package/dist/ui/switch/index.js +7 -10
- package/dist/ui/switch/public.d.ts +1 -1
- package/dist/ui/switch/public.d.ts.map +1 -1
- package/dist/ui/tabs/index.d.ts +9 -7
- package/dist/ui/tabs/index.d.ts.map +1 -1
- package/dist/ui/tabs/index.js +27 -17
- package/dist/ui/tabs/public.d.ts +1 -1
- package/dist/ui/tabs/public.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/ui/menu/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Array, Effect, Match as M, Option, Predicate, Schema as S, String as Str, pipe, } from 'effect';
|
|
2
2
|
import { OptionExt } from '../../effectExtensions';
|
|
3
|
-
import { html } from '../../html';
|
|
4
|
-
import { createLazy } from '../../html/lazy';
|
|
3
|
+
import { createLazy, html } from '../../html';
|
|
5
4
|
import { m } from '../../message';
|
|
6
5
|
import { evo } from '../../struct';
|
|
7
6
|
import * as Task from '../../task';
|
|
@@ -68,8 +67,28 @@ export const MovedPointerOverItem = m('MovedPointerOverItem', {
|
|
|
68
67
|
screenX: S.Number,
|
|
69
68
|
screenY: S.Number,
|
|
70
69
|
});
|
|
71
|
-
/**
|
|
72
|
-
export const
|
|
70
|
+
/** Sent when the focus-items command completes after opening the menu. */
|
|
71
|
+
export const CompletedItemsFocus = m('CompletedItemsFocus');
|
|
72
|
+
/** Sent when the focus-button command completes after closing or selecting. */
|
|
73
|
+
export const CompletedButtonFocus = m('CompletedButtonFocus');
|
|
74
|
+
/** Sent when the scroll lock command completes. */
|
|
75
|
+
export const CompletedScrollLock = m('CompletedScrollLock');
|
|
76
|
+
/** Sent when the scroll unlock command completes. */
|
|
77
|
+
export const CompletedScrollUnlock = m('CompletedScrollUnlock');
|
|
78
|
+
/** Sent when the inert-others command completes. */
|
|
79
|
+
export const CompletedInertSetup = m('CompletedInertSetup');
|
|
80
|
+
/** Sent when the restore-inert command completes. */
|
|
81
|
+
export const CompletedInertTeardown = m('CompletedInertTeardown');
|
|
82
|
+
/** Sent when the scroll-into-view command completes after keyboard activation. */
|
|
83
|
+
export const CompletedScrollIntoView = m('CompletedScrollIntoView');
|
|
84
|
+
/** Sent when the programmatic click command completes. */
|
|
85
|
+
export const CompletedItemClick = m('CompletedItemClick');
|
|
86
|
+
/** Sent when the advance-focus command completes. */
|
|
87
|
+
export const CompletedFocusAdvance = m('CompletedFocusAdvance');
|
|
88
|
+
/** Sent when a mouse click on the button is ignored because pointer-down already handled the toggle. */
|
|
89
|
+
export const IgnoredMouseClick = m('IgnoredMouseClick');
|
|
90
|
+
/** Sent when a Space key-up is captured to prevent page scrolling. */
|
|
91
|
+
export const SuppressedSpaceScroll = m('SuppressedSpaceScroll');
|
|
73
92
|
/** Sent internally when a double-rAF completes, advancing the transition to its animating phase. */
|
|
74
93
|
export const AdvancedTransitionFrame = m('AdvancedTransitionFrame');
|
|
75
94
|
/** Sent internally when all CSS transitions on the menu items container have completed. */
|
|
@@ -91,7 +110,7 @@ export const ReleasedPointerOnItems = m('ReleasedPointerOnItems', {
|
|
|
91
110
|
timeStamp: S.Number,
|
|
92
111
|
});
|
|
93
112
|
/** Union of all messages the menu component can produce. */
|
|
94
|
-
export const Message = S.Union(Opened, Closed, ClosedByTab, ActivatedItem, DeactivatedItem, SelectedItem, MovedPointerOverItem, RequestedItemClick, Searched, ClearedSearch,
|
|
113
|
+
export const Message = S.Union(Opened, Closed, ClosedByTab, ActivatedItem, DeactivatedItem, SelectedItem, MovedPointerOverItem, RequestedItemClick, Searched, ClearedSearch, CompletedItemsFocus, CompletedButtonFocus, CompletedScrollLock, CompletedScrollUnlock, CompletedInertSetup, CompletedInertTeardown, CompletedScrollIntoView, CompletedItemClick, CompletedFocusAdvance, IgnoredMouseClick, SuppressedSpaceScroll, AdvancedTransitionFrame, EndedTransition, DetectedButtonMovement, PressedPointerOnButton, ReleasedPointerOnItems);
|
|
95
114
|
// INIT
|
|
96
115
|
const SEARCH_DEBOUNCE_MILLISECONDS = 350;
|
|
97
116
|
const LEFT_MOUSE_BUTTON = 0;
|
|
@@ -131,13 +150,13 @@ const withUpdateReturn = M.withReturnType();
|
|
|
131
150
|
/** Processes a menu message and returns the next model and commands. */
|
|
132
151
|
export const update = (model, message) => {
|
|
133
152
|
const maybeNextFrame = OptionExt.when(model.isAnimated, Task.nextFrame.pipe(Effect.as(AdvancedTransitionFrame())));
|
|
134
|
-
const maybeLockScroll = OptionExt.when(model.isModal, Task.lockScroll.pipe(Effect.as(
|
|
135
|
-
const maybeUnlockScroll = OptionExt.when(model.isModal, Task.unlockScroll.pipe(Effect.as(
|
|
153
|
+
const maybeLockScroll = OptionExt.when(model.isModal, Task.lockScroll.pipe(Effect.as(CompletedScrollLock())));
|
|
154
|
+
const maybeUnlockScroll = OptionExt.when(model.isModal, Task.unlockScroll.pipe(Effect.as(CompletedScrollUnlock())));
|
|
136
155
|
const maybeInertOthers = OptionExt.when(model.isModal, Task.inertOthers(model.id, [
|
|
137
156
|
buttonSelector(model.id),
|
|
138
157
|
itemsSelector(model.id),
|
|
139
|
-
]).pipe(Effect.as(
|
|
140
|
-
const maybeRestoreInert = OptionExt.when(model.isModal, Task.restoreInert(model.id).pipe(Effect.as(
|
|
158
|
+
]).pipe(Effect.as(CompletedInertSetup())));
|
|
159
|
+
const maybeRestoreInert = OptionExt.when(model.isModal, Task.restoreInert(model.id).pipe(Effect.as(CompletedInertTeardown())));
|
|
141
160
|
return M.value(message).pipe(withUpdateReturn, M.tagsExhaustive({
|
|
142
161
|
Opened: ({ maybeActiveItemIndex }) => {
|
|
143
162
|
const nextModel = evo(model, {
|
|
@@ -154,7 +173,7 @@ export const update = (model, message) => {
|
|
|
154
173
|
});
|
|
155
174
|
return [
|
|
156
175
|
nextModel,
|
|
157
|
-
pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(itemsSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
176
|
+
pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(itemsSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedItemsFocus())))),
|
|
158
177
|
];
|
|
159
178
|
},
|
|
160
179
|
Closed: () => [
|
|
@@ -163,7 +182,7 @@ export const update = (model, message) => {
|
|
|
163
182
|
maybeNextFrame,
|
|
164
183
|
maybeUnlockScroll,
|
|
165
184
|
maybeRestoreInert,
|
|
166
|
-
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
185
|
+
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedButtonFocus())))),
|
|
167
186
|
],
|
|
168
187
|
ClosedByTab: () => [
|
|
169
188
|
closedModel(model),
|
|
@@ -176,7 +195,7 @@ export const update = (model, message) => {
|
|
|
176
195
|
}),
|
|
177
196
|
activationTrigger === 'Keyboard'
|
|
178
197
|
? [
|
|
179
|
-
Task.scrollIntoView(itemSelector(model.id, index)).pipe(Effect.ignore, Effect.as(
|
|
198
|
+
Task.scrollIntoView(itemSelector(model.id, index)).pipe(Effect.ignore, Effect.as(CompletedScrollIntoView())),
|
|
180
199
|
]
|
|
181
200
|
: [],
|
|
182
201
|
],
|
|
@@ -203,12 +222,12 @@ export const update = (model, message) => {
|
|
|
203
222
|
maybeNextFrame,
|
|
204
223
|
maybeUnlockScroll,
|
|
205
224
|
maybeRestoreInert,
|
|
206
|
-
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
225
|
+
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedButtonFocus())))),
|
|
207
226
|
],
|
|
208
227
|
RequestedItemClick: ({ index }) => [
|
|
209
228
|
model,
|
|
210
229
|
[
|
|
211
|
-
Task.clickElement(itemSelector(model.id, index)).pipe(Effect.ignore, Effect.as(
|
|
230
|
+
Task.clickElement(itemSelector(model.id, index)).pipe(Effect.ignore, Effect.as(CompletedItemClick())),
|
|
212
231
|
],
|
|
213
232
|
],
|
|
214
233
|
Searched: ({ key, maybeTargetIndex }) => {
|
|
@@ -264,7 +283,7 @@ export const update = (model, message) => {
|
|
|
264
283
|
maybeNextFrame,
|
|
265
284
|
maybeUnlockScroll,
|
|
266
285
|
maybeRestoreInert,
|
|
267
|
-
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
286
|
+
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedButtonFocus())))),
|
|
268
287
|
];
|
|
269
288
|
}
|
|
270
289
|
const nextModel = evo(withPointerType, {
|
|
@@ -279,7 +298,7 @@ export const update = (model, message) => {
|
|
|
279
298
|
});
|
|
280
299
|
return [
|
|
281
300
|
nextModel,
|
|
282
|
-
pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(itemsSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
301
|
+
pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(itemsSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedItemsFocus())))),
|
|
283
302
|
];
|
|
284
303
|
},
|
|
285
304
|
ReleasedPointerOnItems: ({ screenX, screenY, timeStamp }) => {
|
|
@@ -299,11 +318,21 @@ export const update = (model, message) => {
|
|
|
299
318
|
return [
|
|
300
319
|
model,
|
|
301
320
|
[
|
|
302
|
-
Task.clickElement(itemSelector(model.id, model.maybeActiveItemIndex.value)).pipe(Effect.ignore, Effect.as(
|
|
321
|
+
Task.clickElement(itemSelector(model.id, model.maybeActiveItemIndex.value)).pipe(Effect.ignore, Effect.as(CompletedItemClick())),
|
|
303
322
|
],
|
|
304
323
|
];
|
|
305
324
|
},
|
|
306
|
-
|
|
325
|
+
CompletedItemsFocus: () => [model, []],
|
|
326
|
+
CompletedButtonFocus: () => [model, []],
|
|
327
|
+
CompletedScrollLock: () => [model, []],
|
|
328
|
+
CompletedScrollUnlock: () => [model, []],
|
|
329
|
+
CompletedInertSetup: () => [model, []],
|
|
330
|
+
CompletedInertTeardown: () => [model, []],
|
|
331
|
+
CompletedScrollIntoView: () => [model, []],
|
|
332
|
+
CompletedItemClick: () => [model, []],
|
|
333
|
+
CompletedFocusAdvance: () => [model, []],
|
|
334
|
+
IgnoredMouseClick: () => [model, []],
|
|
335
|
+
SuppressedSpaceScroll: () => [model, []],
|
|
307
336
|
}));
|
|
308
337
|
};
|
|
309
338
|
export { groupContiguous, resolveTypeaheadMatch };
|
|
@@ -311,7 +340,7 @@ const itemId = (id, index) => `${id}-item-${index}`;
|
|
|
311
340
|
/** Renders a headless menu with typeahead search, keyboard navigation, and aria-activedescendant focus management. */
|
|
312
341
|
export const view = (config) => {
|
|
313
342
|
const { div, AriaActiveDescendant, AriaControls, AriaDisabled, AriaExpanded, AriaHasPopup, AriaLabelledBy, Class, DataAttribute, Id, OnBlur, OnClick, OnDestroy, OnInsert, OnKeyDownPreventDefault, OnKeyUpPreventDefault, OnPointerDown, OnPointerLeave, OnPointerMove, OnPointerUp, Role, Style, Tabindex, Type, keyed, } = html();
|
|
314
|
-
const { model: { id, isOpen, transitionState, maybeActiveItemIndex, searchQuery, maybeLastButtonPointerType, }, toMessage, items, itemToConfig, isItemDisabled, itemToSearchText = (item) => item, isButtonDisabled, buttonContent, buttonClassName, itemsClassName, itemsScrollClassName, backdropClassName, className, itemGroupKey, groupToHeading, groupClassName, separatorClassName, anchor, } = config;
|
|
343
|
+
const { model: { id, isOpen, transitionState, maybeActiveItemIndex, searchQuery, maybeLastButtonPointerType, }, toMessage, items, itemToConfig, isItemDisabled, itemToSearchText = (item) => item, isButtonDisabled, buttonContent, buttonClassName, buttonAttributes = [], itemsClassName, itemsAttributes = [], itemsScrollClassName, itemsScrollAttributes = [], backdropClassName, backdropAttributes = [], className, attributes = [], itemGroupKey, groupToHeading, groupClassName, groupAttributes = [], separatorClassName, separatorAttributes = [], anchor, } = config;
|
|
315
344
|
const isLeaving = transitionState === 'LeaveStart' || transitionState === 'LeaveAnimating';
|
|
316
345
|
const isVisible = isOpen || isLeaving;
|
|
317
346
|
const transitionAttributes = M.value(transitionState).pipe(M.when('EnterStart', () => [
|
|
@@ -348,7 +377,7 @@ export const view = (config) => {
|
|
|
348
377
|
const handleButtonClick = () => {
|
|
349
378
|
const isMouse = Option.exists(maybeLastButtonPointerType, type => type === 'mouse');
|
|
350
379
|
if (isMouse) {
|
|
351
|
-
return toMessage(
|
|
380
|
+
return toMessage(IgnoredMouseClick());
|
|
352
381
|
}
|
|
353
382
|
else if (isOpen) {
|
|
354
383
|
return toMessage(Closed());
|
|
@@ -357,7 +386,7 @@ export const view = (config) => {
|
|
|
357
386
|
return toMessage(Opened({ maybeActiveItemIndex: Option.none() }));
|
|
358
387
|
}
|
|
359
388
|
};
|
|
360
|
-
const handleSpaceKeyUp = (key) => OptionExt.when(key === ' ', toMessage(
|
|
389
|
+
const handleSpaceKeyUp = (key) => OptionExt.when(key === ' ', toMessage(SuppressedSpaceScroll()));
|
|
361
390
|
const resolveActiveIndex = keyToIndex('ArrowDown', 'ArrowUp', items.length, Option.getOrElse(maybeActiveItemIndex, () => 0), isDisabled);
|
|
362
391
|
const searchForKey = (key) => {
|
|
363
392
|
const nextQuery = searchQuery + key;
|
|
@@ -371,10 +400,9 @@ export const view = (config) => {
|
|
|
371
400
|
activationTrigger: 'Keyboard',
|
|
372
401
|
})))), M.when(isPrintableKey, () => searchForKey(key)), M.orElse(() => Option.none()));
|
|
373
402
|
const handleItemsPointerUp = (screenX, screenY, pointerType, timeStamp) => OptionExt.when(pointerType === 'mouse', toMessage(ReleasedPointerOnItems({ screenX, screenY, timeStamp })));
|
|
374
|
-
const
|
|
403
|
+
const resolvedButtonAttributes = [
|
|
375
404
|
Id(`${id}-button`),
|
|
376
405
|
Type('button'),
|
|
377
|
-
Class(buttonClassName),
|
|
378
406
|
AriaHasPopup('menu'),
|
|
379
407
|
AriaExpanded(isVisible),
|
|
380
408
|
AriaControls(`${id}-items`),
|
|
@@ -387,6 +415,8 @@ export const view = (config) => {
|
|
|
387
415
|
OnClick(handleButtonClick()),
|
|
388
416
|
]),
|
|
389
417
|
...(isVisible ? [DataAttribute('open', '')] : []),
|
|
418
|
+
...(buttonClassName ? [Class(buttonClassName)] : []),
|
|
419
|
+
...buttonAttributes,
|
|
390
420
|
];
|
|
391
421
|
const maybeActiveDescendant = Option.match(maybeActiveItemIndex, {
|
|
392
422
|
onNone: () => [],
|
|
@@ -408,7 +438,6 @@ export const view = (config) => {
|
|
|
408
438
|
AriaLabelledBy(`${id}-button`),
|
|
409
439
|
...maybeActiveDescendant,
|
|
410
440
|
Tabindex(0),
|
|
411
|
-
Class(itemsClassName),
|
|
412
441
|
...anchorAttributes,
|
|
413
442
|
...transitionAttributes,
|
|
414
443
|
...(isLeaving
|
|
@@ -419,6 +448,8 @@ export const view = (config) => {
|
|
|
419
448
|
OnPointerUp(handleItemsPointerUp),
|
|
420
449
|
OnBlur(toMessage(ClosedByTab())),
|
|
421
450
|
]),
|
|
451
|
+
...(itemsClassName ? [Class(itemsClassName)] : []),
|
|
452
|
+
...itemsAttributes,
|
|
422
453
|
];
|
|
423
454
|
const menuItems = Array.map(items, (item, index) => {
|
|
424
455
|
const isActiveItem = Option.exists(maybeActiveItemIndex, activeIndex => activeIndex === index);
|
|
@@ -431,7 +462,6 @@ export const view = (config) => {
|
|
|
431
462
|
return keyed('div')(itemId(id, index), [
|
|
432
463
|
Id(itemId(id, index)),
|
|
433
464
|
Role('menuitem'),
|
|
434
|
-
Class(itemConfig.className),
|
|
435
465
|
...(isActiveItem ? [DataAttribute('active', '')] : []),
|
|
436
466
|
...(isDisabledItem
|
|
437
467
|
? [AriaDisabled(true), DataAttribute('disabled', '')]
|
|
@@ -447,6 +477,7 @@ export const view = (config) => {
|
|
|
447
477
|
OnPointerLeave(pointerType => OptionExt.when(pointerType !== 'touch', toMessage(DeactivatedItem()))),
|
|
448
478
|
]
|
|
449
479
|
: []),
|
|
480
|
+
...(itemConfig.className ? [Class(itemConfig.className)] : []),
|
|
450
481
|
], [itemConfig.content]);
|
|
451
482
|
});
|
|
452
483
|
const renderGroupedItems = () => {
|
|
@@ -463,7 +494,11 @@ export const view = (config) => {
|
|
|
463
494
|
const headingElement = Option.match(maybeHeading, {
|
|
464
495
|
onNone: () => [],
|
|
465
496
|
onSome: heading => [
|
|
466
|
-
keyed('div')(headingId, [
|
|
497
|
+
keyed('div')(headingId, [
|
|
498
|
+
Id(headingId),
|
|
499
|
+
Role('presentation'),
|
|
500
|
+
...(heading.className ? [Class(heading.className)] : []),
|
|
501
|
+
], [heading.content]),
|
|
467
502
|
],
|
|
468
503
|
});
|
|
469
504
|
const groupContent = [...headingElement, ...segment.items];
|
|
@@ -471,22 +506,35 @@ export const view = (config) => {
|
|
|
471
506
|
Role('group'),
|
|
472
507
|
...(Option.isSome(maybeHeading) ? [AriaLabelledBy(headingId)] : []),
|
|
473
508
|
...(groupClassName ? [Class(groupClassName)] : []),
|
|
509
|
+
...groupAttributes,
|
|
474
510
|
], groupContent);
|
|
475
|
-
const separator = segmentIndex > 0 &&
|
|
511
|
+
const separator = segmentIndex > 0 &&
|
|
512
|
+
(separatorClassName ||
|
|
513
|
+
Array.isNonEmptyReadonlyArray(separatorAttributes))
|
|
476
514
|
? [
|
|
477
|
-
keyed('div')(`${id}-separator-${segmentIndex}`, [
|
|
515
|
+
keyed('div')(`${id}-separator-${segmentIndex}`, [
|
|
516
|
+
Role('separator'),
|
|
517
|
+
...(separatorClassName ? [Class(separatorClassName)] : []),
|
|
518
|
+
...separatorAttributes,
|
|
519
|
+
], []),
|
|
478
520
|
]
|
|
479
521
|
: [];
|
|
480
522
|
return [...separator, groupElement];
|
|
481
523
|
});
|
|
482
524
|
};
|
|
483
525
|
const backdrop = keyed('div')(`${id}-backdrop`, [
|
|
484
|
-
Class(backdropClassName),
|
|
485
526
|
...(isLeaving ? [] : [OnClick(toMessage(Closed()))]),
|
|
527
|
+
...(backdropClassName ? [Class(backdropClassName)] : []),
|
|
528
|
+
...backdropAttributes,
|
|
486
529
|
], []);
|
|
487
530
|
const renderedItems = renderGroupedItems();
|
|
488
|
-
const scrollableItems = itemsScrollClassName
|
|
489
|
-
? [
|
|
531
|
+
const scrollableItems = itemsScrollClassName || Array.isNonEmptyReadonlyArray(itemsScrollAttributes)
|
|
532
|
+
? [
|
|
533
|
+
div([
|
|
534
|
+
...(itemsScrollClassName ? [Class(itemsScrollClassName)] : []),
|
|
535
|
+
...itemsScrollAttributes,
|
|
536
|
+
], renderedItems),
|
|
537
|
+
]
|
|
490
538
|
: renderedItems;
|
|
491
539
|
const visibleContent = [
|
|
492
540
|
backdrop,
|
|
@@ -494,10 +542,11 @@ export const view = (config) => {
|
|
|
494
542
|
];
|
|
495
543
|
const wrapperAttributes = [
|
|
496
544
|
...(className ? [Class(className)] : []),
|
|
545
|
+
...attributes,
|
|
497
546
|
...(isVisible ? [DataAttribute('open', '')] : []),
|
|
498
547
|
];
|
|
499
548
|
return div(wrapperAttributes, [
|
|
500
|
-
keyed('button')(`${id}-button`,
|
|
549
|
+
keyed('button')(`${id}-button`, resolvedButtonAttributes, [buttonContent]),
|
|
501
550
|
...(isVisible ? visibleContent : []),
|
|
502
551
|
]);
|
|
503
552
|
};
|
package/dist/ui/menu/public.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { init, update, view, lazy, Model, Message } from './index';
|
|
2
2
|
export { TransitionState } from '../transition';
|
|
3
|
-
export type { ActivationTrigger, Opened, Closed, ClosedByTab, ActivatedItem, DeactivatedItem, SelectedItem, MovedPointerOverItem, RequestedItemClick, Searched, ClearedSearch, PressedPointerOnButton, ReleasedPointerOnItems,
|
|
3
|
+
export type { ActivationTrigger, Opened, Closed, ClosedByTab, ActivatedItem, DeactivatedItem, SelectedItem, MovedPointerOverItem, RequestedItemClick, Searched, ClearedSearch, PressedPointerOnButton, ReleasedPointerOnItems, IgnoredMouseClick, SuppressedSpaceScroll, AdvancedTransitionFrame, EndedTransition, DetectedButtonMovement, InitConfig, ViewConfig, ItemConfig, GroupHeading, } from './index';
|
|
4
4
|
export type { AnchorConfig } from '../anchor';
|
|
5
5
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/menu/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,YAAY,EACV,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,WAAW,EACX,aAAa,EACb,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,sBAAsB,EACtB,sBAAsB,EACtB,
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/menu/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,YAAY,EACV,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,WAAW,EACX,aAAa,EACb,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,sBAAsB,EACtB,UAAU,EACV,UAAU,EACV,UAAU,EACV,YAAY,GACb,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Schema as S } from 'effect';
|
|
2
2
|
import type { Command } from '../../command';
|
|
3
|
-
import { type Html } from '../../html';
|
|
3
|
+
import { type Attribute, type Html } from '../../html';
|
|
4
4
|
import type { AnchorConfig } from '../anchor';
|
|
5
5
|
/** Schema for the popover component's state, tracking open/closed status and transition animation. */
|
|
6
6
|
export declare const Model: S.Struct<{
|
|
@@ -23,8 +23,22 @@ export declare const PressedPointerOnButton: import("../../schema").CallableTagg
|
|
|
23
23
|
pointerType: typeof S.String;
|
|
24
24
|
button: typeof S.Number;
|
|
25
25
|
}>;
|
|
26
|
-
/**
|
|
27
|
-
export declare const
|
|
26
|
+
/** Sent when the focus-panel command completes after opening the popover. */
|
|
27
|
+
export declare const CompletedPanelFocus: import("../../schema").CallableTaggedStruct<"CompletedPanelFocus", {}>;
|
|
28
|
+
/** Sent when the focus-button command completes after closing. */
|
|
29
|
+
export declare const CompletedButtonFocus: import("../../schema").CallableTaggedStruct<"CompletedButtonFocus", {}>;
|
|
30
|
+
/** Sent when the scroll lock command completes. */
|
|
31
|
+
export declare const CompletedScrollLock: import("../../schema").CallableTaggedStruct<"CompletedScrollLock", {}>;
|
|
32
|
+
/** Sent when the scroll unlock command completes. */
|
|
33
|
+
export declare const CompletedScrollUnlock: import("../../schema").CallableTaggedStruct<"CompletedScrollUnlock", {}>;
|
|
34
|
+
/** Sent when the inert-others command completes. */
|
|
35
|
+
export declare const CompletedInertSetup: import("../../schema").CallableTaggedStruct<"CompletedInertSetup", {}>;
|
|
36
|
+
/** Sent when the restore-inert command completes. */
|
|
37
|
+
export declare const CompletedInertTeardown: import("../../schema").CallableTaggedStruct<"CompletedInertTeardown", {}>;
|
|
38
|
+
/** Sent when a mouse click on the button is ignored because pointer-down already handled the toggle. */
|
|
39
|
+
export declare const IgnoredMouseClick: import("../../schema").CallableTaggedStruct<"IgnoredMouseClick", {}>;
|
|
40
|
+
/** Sent when a Space key-up is captured to prevent page scrolling. */
|
|
41
|
+
export declare const SuppressedSpaceScroll: import("../../schema").CallableTaggedStruct<"SuppressedSpaceScroll", {}>;
|
|
28
42
|
/** Sent internally when a double-rAF completes, advancing the transition to its animating phase. */
|
|
29
43
|
export declare const AdvancedTransitionFrame: import("../../schema").CallableTaggedStruct<"AdvancedTransitionFrame", {}>;
|
|
30
44
|
/** Sent internally when all CSS transitions on the popover panel have completed. */
|
|
@@ -35,15 +49,13 @@ export declare const DetectedButtonMovement: import("../../schema").CallableTagg
|
|
|
35
49
|
export declare const Message: S.Union<[import("../../schema").CallableTaggedStruct<"Opened", {}>, import("../../schema").CallableTaggedStruct<"Closed", {}>, import("../../schema").CallableTaggedStruct<"ClosedByTab", {}>, import("../../schema").CallableTaggedStruct<"PressedPointerOnButton", {
|
|
36
50
|
pointerType: typeof S.String;
|
|
37
51
|
button: typeof S.Number;
|
|
38
|
-
}>, import("../../schema").CallableTaggedStruct<"
|
|
52
|
+
}>, import("../../schema").CallableTaggedStruct<"CompletedPanelFocus", {}>, import("../../schema").CallableTaggedStruct<"CompletedButtonFocus", {}>, import("../../schema").CallableTaggedStruct<"CompletedScrollLock", {}>, import("../../schema").CallableTaggedStruct<"CompletedScrollUnlock", {}>, import("../../schema").CallableTaggedStruct<"CompletedInertSetup", {}>, import("../../schema").CallableTaggedStruct<"CompletedInertTeardown", {}>, import("../../schema").CallableTaggedStruct<"IgnoredMouseClick", {}>, import("../../schema").CallableTaggedStruct<"SuppressedSpaceScroll", {}>, import("../../schema").CallableTaggedStruct<"AdvancedTransitionFrame", {}>, import("../../schema").CallableTaggedStruct<"EndedTransition", {}>, import("../../schema").CallableTaggedStruct<"DetectedButtonMovement", {}>]>;
|
|
39
53
|
export type Opened = typeof Opened.Type;
|
|
40
54
|
export type Closed = typeof Closed.Type;
|
|
41
55
|
export type ClosedByTab = typeof ClosedByTab.Type;
|
|
42
56
|
export type PressedPointerOnButton = typeof PressedPointerOnButton.Type;
|
|
43
|
-
export type
|
|
44
|
-
export type
|
|
45
|
-
export type EndedTransition = typeof EndedTransition.Type;
|
|
46
|
-
export type DetectedButtonMovement = typeof DetectedButtonMovement.Type;
|
|
57
|
+
export type IgnoredMouseClick = typeof IgnoredMouseClick.Type;
|
|
58
|
+
export type SuppressedSpaceScroll = typeof SuppressedSpaceScroll.Type;
|
|
47
59
|
export type Message = typeof Message.Type;
|
|
48
60
|
/** Configuration for creating a popover model with `init`. `isAnimated` enables CSS transition coordination (default `false`). `isModal` locks page scroll and inerts other elements when open (default `false`). */
|
|
49
61
|
export type InitConfig = Readonly<{
|
|
@@ -59,15 +71,19 @@ export declare const update: (model: Model, message: Message) => UpdateReturn;
|
|
|
59
71
|
/** Configuration for rendering a popover with `view`. */
|
|
60
72
|
export type ViewConfig<Message> = Readonly<{
|
|
61
73
|
model: Model;
|
|
62
|
-
toMessage: (message: Opened | Closed | ClosedByTab | PressedPointerOnButton |
|
|
74
|
+
toMessage: (message: Opened | Closed | ClosedByTab | PressedPointerOnButton | IgnoredMouseClick | SuppressedSpaceScroll) => Message;
|
|
63
75
|
anchor: AnchorConfig;
|
|
64
76
|
buttonContent: Html;
|
|
65
|
-
buttonClassName
|
|
77
|
+
buttonClassName?: string;
|
|
78
|
+
buttonAttributes?: ReadonlyArray<Attribute<Message>>;
|
|
66
79
|
panelContent: Html;
|
|
67
|
-
panelClassName
|
|
68
|
-
|
|
80
|
+
panelClassName?: string;
|
|
81
|
+
panelAttributes?: ReadonlyArray<Attribute<Message>>;
|
|
82
|
+
backdropClassName?: string;
|
|
83
|
+
backdropAttributes?: ReadonlyArray<Attribute<Message>>;
|
|
69
84
|
isDisabled?: boolean;
|
|
70
85
|
className?: string;
|
|
86
|
+
attributes?: ReadonlyArray<Attribute<Message>>;
|
|
71
87
|
}>;
|
|
72
88
|
/** Renders a headless popover with a trigger button and a floating panel. Uses the disclosure ARIA pattern (aria-expanded + aria-controls) with no role on the panel. */
|
|
73
89
|
export declare const view: <Message>(config: ViewConfig<Message>) => Html;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/popover/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqC,MAAM,IAAI,CAAC,EAAQ,MAAM,QAAQ,CAAA;AAE7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C,OAAO,EAAE,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/popover/index.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/popover/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Array, Effect, Match as M, Option, Schema as S, pipe } from 'effect';
|
|
2
2
|
import { OptionExt } from '../../effectExtensions';
|
|
3
|
-
import { html } from '../../html';
|
|
4
|
-
import { createLazy } from '../../html/lazy';
|
|
3
|
+
import { createLazy, html } from '../../html';
|
|
5
4
|
import { m } from '../../message';
|
|
6
5
|
import { evo } from '../../struct';
|
|
7
6
|
import * as Task from '../../task';
|
|
@@ -29,8 +28,22 @@ export const PressedPointerOnButton = m('PressedPointerOnButton', {
|
|
|
29
28
|
pointerType: S.String,
|
|
30
29
|
button: S.Number,
|
|
31
30
|
});
|
|
32
|
-
/**
|
|
33
|
-
export const
|
|
31
|
+
/** Sent when the focus-panel command completes after opening the popover. */
|
|
32
|
+
export const CompletedPanelFocus = m('CompletedPanelFocus');
|
|
33
|
+
/** Sent when the focus-button command completes after closing. */
|
|
34
|
+
export const CompletedButtonFocus = m('CompletedButtonFocus');
|
|
35
|
+
/** Sent when the scroll lock command completes. */
|
|
36
|
+
export const CompletedScrollLock = m('CompletedScrollLock');
|
|
37
|
+
/** Sent when the scroll unlock command completes. */
|
|
38
|
+
export const CompletedScrollUnlock = m('CompletedScrollUnlock');
|
|
39
|
+
/** Sent when the inert-others command completes. */
|
|
40
|
+
export const CompletedInertSetup = m('CompletedInertSetup');
|
|
41
|
+
/** Sent when the restore-inert command completes. */
|
|
42
|
+
export const CompletedInertTeardown = m('CompletedInertTeardown');
|
|
43
|
+
/** Sent when a mouse click on the button is ignored because pointer-down already handled the toggle. */
|
|
44
|
+
export const IgnoredMouseClick = m('IgnoredMouseClick');
|
|
45
|
+
/** Sent when a Space key-up is captured to prevent page scrolling. */
|
|
46
|
+
export const SuppressedSpaceScroll = m('SuppressedSpaceScroll');
|
|
34
47
|
/** Sent internally when a double-rAF completes, advancing the transition to its animating phase. */
|
|
35
48
|
export const AdvancedTransitionFrame = m('AdvancedTransitionFrame');
|
|
36
49
|
/** Sent internally when all CSS transitions on the popover panel have completed. */
|
|
@@ -38,7 +51,7 @@ export const EndedTransition = m('EndedTransition');
|
|
|
38
51
|
/** Sent internally when the popover button moves in the viewport during a leave transition, cancelling the animation. */
|
|
39
52
|
export const DetectedButtonMovement = m('DetectedButtonMovement');
|
|
40
53
|
/** Union of all messages the popover component can produce. */
|
|
41
|
-
export const Message = S.Union(Opened, Closed, ClosedByTab, PressedPointerOnButton,
|
|
54
|
+
export const Message = S.Union(Opened, Closed, ClosedByTab, PressedPointerOnButton, CompletedPanelFocus, CompletedButtonFocus, CompletedScrollLock, CompletedScrollUnlock, CompletedInertSetup, CompletedInertTeardown, IgnoredMouseClick, SuppressedSpaceScroll, AdvancedTransitionFrame, EndedTransition, DetectedButtonMovement);
|
|
42
55
|
// INIT
|
|
43
56
|
const LEFT_MOUSE_BUTTON = 0;
|
|
44
57
|
/** Creates an initial popover model from a config. Defaults to closed. */
|
|
@@ -62,13 +75,13 @@ const withUpdateReturn = M.withReturnType();
|
|
|
62
75
|
/** Processes a popover message and returns the next model and commands. */
|
|
63
76
|
export const update = (model, message) => {
|
|
64
77
|
const maybeNextFrame = OptionExt.when(model.isAnimated, Task.nextFrame.pipe(Effect.as(AdvancedTransitionFrame())));
|
|
65
|
-
const maybeLockScroll = OptionExt.when(model.isModal, Task.lockScroll.pipe(Effect.as(
|
|
66
|
-
const maybeUnlockScroll = OptionExt.when(model.isModal, Task.unlockScroll.pipe(Effect.as(
|
|
78
|
+
const maybeLockScroll = OptionExt.when(model.isModal, Task.lockScroll.pipe(Effect.as(CompletedScrollLock())));
|
|
79
|
+
const maybeUnlockScroll = OptionExt.when(model.isModal, Task.unlockScroll.pipe(Effect.as(CompletedScrollUnlock())));
|
|
67
80
|
const maybeInertOthers = OptionExt.when(model.isModal, Task.inertOthers(model.id, [
|
|
68
81
|
buttonSelector(model.id),
|
|
69
82
|
panelSelector(model.id),
|
|
70
|
-
]).pipe(Effect.as(
|
|
71
|
-
const maybeRestoreInert = OptionExt.when(model.isModal, Task.restoreInert(model.id).pipe(Effect.as(
|
|
83
|
+
]).pipe(Effect.as(CompletedInertSetup())));
|
|
84
|
+
const maybeRestoreInert = OptionExt.when(model.isModal, Task.restoreInert(model.id).pipe(Effect.as(CompletedInertTeardown())));
|
|
72
85
|
return M.value(message).pipe(withUpdateReturn, M.tagsExhaustive({
|
|
73
86
|
Opened: () => {
|
|
74
87
|
const nextModel = evo(model, {
|
|
@@ -77,7 +90,7 @@ export const update = (model, message) => {
|
|
|
77
90
|
});
|
|
78
91
|
return [
|
|
79
92
|
nextModel,
|
|
80
|
-
pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(panelSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
93
|
+
pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(panelSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedPanelFocus())))),
|
|
81
94
|
];
|
|
82
95
|
},
|
|
83
96
|
Closed: () => [
|
|
@@ -86,7 +99,7 @@ export const update = (model, message) => {
|
|
|
86
99
|
maybeNextFrame,
|
|
87
100
|
maybeUnlockScroll,
|
|
88
101
|
maybeRestoreInert,
|
|
89
|
-
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
102
|
+
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedButtonFocus())))),
|
|
90
103
|
],
|
|
91
104
|
ClosedByTab: () => [
|
|
92
105
|
closedModel(model),
|
|
@@ -106,7 +119,7 @@ export const update = (model, message) => {
|
|
|
106
119
|
maybeNextFrame,
|
|
107
120
|
maybeUnlockScroll,
|
|
108
121
|
maybeRestoreInert,
|
|
109
|
-
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
122
|
+
]), Array.prepend(Task.focus(buttonSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedButtonFocus())))),
|
|
110
123
|
];
|
|
111
124
|
}
|
|
112
125
|
const nextModel = evo(withPointerType, {
|
|
@@ -115,7 +128,7 @@ export const update = (model, message) => {
|
|
|
115
128
|
});
|
|
116
129
|
return [
|
|
117
130
|
nextModel,
|
|
118
|
-
pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(panelSelector(model.id)).pipe(Effect.ignore, Effect.as(
|
|
131
|
+
pipe(Array.getSomes([maybeNextFrame, maybeLockScroll, maybeInertOthers]), Array.prepend(Task.focus(panelSelector(model.id)).pipe(Effect.ignore, Effect.as(CompletedPanelFocus())))),
|
|
119
132
|
];
|
|
120
133
|
},
|
|
121
134
|
AdvancedTransitionFrame: () => M.value(model.transitionState).pipe(withUpdateReturn, M.when('EnterStart', () => [
|
|
@@ -137,13 +150,20 @@ export const update = (model, message) => {
|
|
|
137
150
|
evo(model, { transitionState: () => 'Idle' }),
|
|
138
151
|
[],
|
|
139
152
|
]), M.orElse(() => [model, []])),
|
|
140
|
-
|
|
153
|
+
CompletedPanelFocus: () => [model, []],
|
|
154
|
+
CompletedButtonFocus: () => [model, []],
|
|
155
|
+
CompletedScrollLock: () => [model, []],
|
|
156
|
+
CompletedScrollUnlock: () => [model, []],
|
|
157
|
+
CompletedInertSetup: () => [model, []],
|
|
158
|
+
CompletedInertTeardown: () => [model, []],
|
|
159
|
+
IgnoredMouseClick: () => [model, []],
|
|
160
|
+
SuppressedSpaceScroll: () => [model, []],
|
|
141
161
|
}));
|
|
142
162
|
};
|
|
143
163
|
/** Renders a headless popover with a trigger button and a floating panel. Uses the disclosure ARIA pattern (aria-expanded + aria-controls) with no role on the panel. */
|
|
144
164
|
export const view = (config) => {
|
|
145
165
|
const { div, AriaControls, AriaDisabled, AriaExpanded, Class, DataAttribute, Id, OnBlur, OnClick, OnDestroy, OnInsert, OnKeyDownPreventDefault, OnKeyUpPreventDefault, OnPointerDown, Style, Tabindex, Type, keyed, } = html();
|
|
146
|
-
const { model: { id, isOpen, transitionState, maybeLastButtonPointerType }, toMessage, anchor, buttonContent, buttonClassName, panelContent, panelClassName, backdropClassName, isDisabled, className, } = config;
|
|
166
|
+
const { model: { id, isOpen, transitionState, maybeLastButtonPointerType }, toMessage, anchor, buttonContent, buttonClassName, buttonAttributes = [], panelContent, panelClassName, panelAttributes = [], backdropClassName, backdropAttributes = [], isDisabled, className, attributes = [], } = config;
|
|
147
167
|
const isLeaving = transitionState === 'LeaveStart' || transitionState === 'LeaveAnimating';
|
|
148
168
|
const isVisible = isOpen || isLeaving;
|
|
149
169
|
const transitionAttributes = M.value(transitionState).pipe(M.when('EnterStart', () => [
|
|
@@ -169,7 +189,7 @@ export const view = (config) => {
|
|
|
169
189
|
const handleButtonClick = () => {
|
|
170
190
|
const isMouse = Option.exists(maybeLastButtonPointerType, type => type === 'mouse');
|
|
171
191
|
if (isMouse) {
|
|
172
|
-
return toMessage(
|
|
192
|
+
return toMessage(IgnoredMouseClick());
|
|
173
193
|
}
|
|
174
194
|
else if (isOpen) {
|
|
175
195
|
return toMessage(Closed());
|
|
@@ -178,12 +198,11 @@ export const view = (config) => {
|
|
|
178
198
|
return toMessage(Opened());
|
|
179
199
|
}
|
|
180
200
|
};
|
|
181
|
-
const handleSpaceKeyUp = (key) => OptionExt.when(key === ' ', toMessage(
|
|
201
|
+
const handleSpaceKeyUp = (key) => OptionExt.when(key === ' ', toMessage(SuppressedSpaceScroll()));
|
|
182
202
|
const handlePanelKeyDown = (key) => M.value(key).pipe(M.when('Escape', () => Option.some(toMessage(Closed()))), M.orElse(() => Option.none()));
|
|
183
|
-
const
|
|
203
|
+
const resolvedButtonAttributes = [
|
|
184
204
|
Id(`${id}-button`),
|
|
185
205
|
Type('button'),
|
|
186
|
-
Class(buttonClassName),
|
|
187
206
|
AriaExpanded(isVisible),
|
|
188
207
|
AriaControls(`${id}-panel`),
|
|
189
208
|
...(isDisabled
|
|
@@ -195,6 +214,8 @@ export const view = (config) => {
|
|
|
195
214
|
OnClick(handleButtonClick()),
|
|
196
215
|
]),
|
|
197
216
|
...(isVisible ? [DataAttribute('open', '')] : []),
|
|
217
|
+
...(buttonClassName ? [Class(buttonClassName)] : []),
|
|
218
|
+
...buttonAttributes,
|
|
198
219
|
];
|
|
199
220
|
const hooks = anchorHooks({
|
|
200
221
|
buttonId: `${id}-button`,
|
|
@@ -206,10 +227,9 @@ export const view = (config) => {
|
|
|
206
227
|
OnInsert(hooks.onInsert),
|
|
207
228
|
OnDestroy(hooks.onDestroy),
|
|
208
229
|
];
|
|
209
|
-
const
|
|
230
|
+
const resolvedPanelAttributes = [
|
|
210
231
|
Id(`${id}-panel`),
|
|
211
232
|
Tabindex(0),
|
|
212
|
-
Class(panelClassName),
|
|
213
233
|
...anchorAttributes,
|
|
214
234
|
...transitionAttributes,
|
|
215
235
|
...(isLeaving
|
|
@@ -218,21 +238,27 @@ export const view = (config) => {
|
|
|
218
238
|
OnKeyDownPreventDefault(handlePanelKeyDown),
|
|
219
239
|
OnBlur(toMessage(ClosedByTab())),
|
|
220
240
|
]),
|
|
241
|
+
...(panelClassName ? [Class(panelClassName)] : []),
|
|
242
|
+
...panelAttributes,
|
|
221
243
|
];
|
|
222
244
|
const backdrop = keyed('div')(`${id}-backdrop`, [
|
|
223
|
-
Class(backdropClassName),
|
|
224
245
|
...(isLeaving ? [] : [OnClick(toMessage(Closed()))]),
|
|
246
|
+
...(backdropClassName ? [Class(backdropClassName)] : []),
|
|
247
|
+
...backdropAttributes,
|
|
225
248
|
], []);
|
|
226
249
|
const visibleContent = [
|
|
227
250
|
backdrop,
|
|
228
|
-
keyed('div')(`${id}-panel-container`,
|
|
251
|
+
keyed('div')(`${id}-panel-container`, resolvedPanelAttributes, [
|
|
252
|
+
panelContent,
|
|
253
|
+
]),
|
|
229
254
|
];
|
|
230
255
|
const wrapperAttributes = [
|
|
231
256
|
...(className ? [Class(className)] : []),
|
|
257
|
+
...attributes,
|
|
232
258
|
...(isVisible ? [DataAttribute('open', '')] : []),
|
|
233
259
|
];
|
|
234
260
|
return div(wrapperAttributes, [
|
|
235
|
-
keyed('button')(`${id}-button`,
|
|
261
|
+
keyed('button')(`${id}-button`, resolvedButtonAttributes, [buttonContent]),
|
|
236
262
|
...(isVisible ? visibleContent : []),
|
|
237
263
|
]);
|
|
238
264
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { init, update, view, lazy, Model, Message } from './index';
|
|
2
2
|
export { TransitionState } from '../transition';
|
|
3
|
-
export type { Opened, Closed, ClosedByTab, PressedPointerOnButton,
|
|
3
|
+
export type { Opened, Closed, ClosedByTab, PressedPointerOnButton, IgnoredMouseClick, SuppressedSpaceScroll, InitConfig, ViewConfig, } from './index';
|
|
4
4
|
export type { AnchorConfig } from '../anchor';
|
|
5
5
|
//# sourceMappingURL=public.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/popover/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,YAAY,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,sBAAsB,EACtB,
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/popover/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,YAAY,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,UAAU,GACX,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA"}
|