@momentum-design/components 0.104.17 → 0.105.1
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/browser/index.js +292 -275
- package/dist/browser/index.js.map +4 -4
- package/dist/components/input/input.types.d.ts +8 -5
- package/dist/components/listbox/index.d.ts +7 -0
- package/dist/components/listbox/index.js +4 -0
- package/dist/components/listbox/listbox.component.d.ts +94 -0
- package/dist/components/listbox/listbox.component.js +198 -0
- package/dist/components/listbox/listbox.constants.d.ts +2 -0
- package/dist/components/listbox/listbox.constants.js +3 -0
- package/dist/components/listbox/listbox.styles.d.ts +2 -0
- package/dist/components/listbox/listbox.styles.js +20 -0
- package/dist/components/listbox/listbox.types.d.ts +7 -0
- package/dist/components/listbox/listbox.types.js +1 -0
- package/dist/components/listitem/listitem.component.d.ts +1 -2
- package/dist/components/listitem/listitem.component.js +4 -6
- package/dist/components/option/option.component.js +1 -0
- package/dist/custom-elements.json +2765 -1992
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/react/index.d.ts +4 -3
- package/dist/react/index.js +4 -3
- package/dist/react/input/index.d.ts +3 -3
- package/dist/react/listbox/index.d.ts +30 -0
- package/dist/react/listbox/index.js +38 -0
- package/dist/react/password/index.d.ts +3 -3
- package/dist/react/searchfield/index.d.ts +3 -3
- package/dist/utils/controllers/ElementStore.d.ts +153 -0
- package/dist/utils/controllers/ElementStore.js +171 -0
- package/dist/utils/dom.d.ts +13 -0
- package/dist/utils/dom.js +14 -0
- package/dist/utils/mixins/ItemCollectionMixin.d.ts +68 -0
- package/dist/utils/mixins/ItemCollectionMixin.js +89 -0
- package/dist/utils/mixins/ListNavigationMixin.d.ts +28 -0
- package/dist/utils/mixins/ListNavigationMixin.js +199 -0
- package/dist/utils/mixins/lifecycle/CaptureDestroyEventForChildElement.d.ts +30 -0
- package/dist/utils/mixins/lifecycle/CaptureDestroyEventForChildElement.js +65 -0
- package/dist/utils/mixins/lifecycle/LifeCycleMixin.d.ts +33 -0
- package/dist/utils/mixins/lifecycle/LifeCycleMixin.js +41 -0
- package/dist/utils/mixins/lifecycle/LifeCycleModifiedEvent.d.ts +19 -0
- package/dist/utils/mixins/lifecycle/LifeCycleModifiedEvent.js +1 -0
- package/dist/utils/mixins/lifecycle/lifecycle.contants.d.ts +5 -0
- package/dist/utils/mixins/lifecycle/lifecycle.contants.js +5 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
@@ -76,6 +76,7 @@ import Tooltip from './components/tooltip';
|
|
76
76
|
import VirtualizedList from './components/virtualizedlist';
|
77
77
|
import Combobox from './components/combobox';
|
78
78
|
import Slider from './components/slider';
|
79
|
+
import ListBox from './components/listbox';
|
79
80
|
import type { BadgeType } from './components/badge/badge.types';
|
80
81
|
import type { ButtonColor, ButtonVariant, IconButtonSize, PillButtonSize } from './components/button/button.types';
|
81
82
|
import type { PopoverPlacement } from './components/popover/popover.types';
|
@@ -86,9 +87,10 @@ import type { TextType as TypewriterType } from './components/typewriter/typewri
|
|
86
87
|
import type { MenuPopoverActionEvent, MenuPopoverChangeEvent } from './components/menupopover/menupopover.types';
|
87
88
|
import type { SelectChangeEvent, SelectInputEvent } from './components/select/select.types';
|
88
89
|
import type { MenuSectionChangeEvent } from './components/menusection/menusection.types';
|
90
|
+
import type { InputInputEvent, InputChangeEvent, InputFocusEvent, InputBlurEvent, InputClearEvent } from './components/input/input.types';
|
89
91
|
import { BUTTON_COLORS, BUTTON_VARIANTS, ICON_BUTTON_SIZES, PILL_BUTTON_SIZES } from './components/button/button.constants';
|
90
92
|
import { SKELETON_VARIANTS } from './components/skeleton/skeleton.constants';
|
91
93
|
import { inMemoryCache, webAPIIconsCache } from './utils/icon-cache';
|
92
|
-
export { Accordion, AccordionButton, AccordionGroup, AlertChip, Animation, Appheader, Avatar, AvatarButton, Badge, Brandvisual, Bullet, Button, ButtonGroup, ButtonLink, Card, CardButton, CardCheckbox, CardRadio, Checkbox, Chip, Coachmark, Dialog, Divider, FilterChip, FormfieldGroup, Icon, IconProvider, Input, InputChip, Link, LinkButton, Linksimple, List, Listheader, ListItem, Marker, MenuBar, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuPopover, MenuSection, NavMenuItem, OptGroup, Option, Password, Popover, Presence, Progressbar, Progressspinner, Radio, RadioGroup, ScreenreaderAnnouncer, Searchfield, Select, SelectListbox, SideNavigation, Skeleton, Spinner, StaticCheckbox, StaticRadio, StaticToggle, Stepper, StepperConnector, StepperItem, Tab, TabList, Text, Textarea, ThemeProvider, Toast, Toggle, Typewriter, ToggleTip, Tooltip, VirtualizedList, Combobox, Slider, };
|
93
|
-
export type { BadgeType, ButtonColor, ButtonVariant, IconButtonSize, MenuPopoverActionEvent, MenuPopoverChangeEvent, MenuSectionChangeEvent, PillButtonSize, PopoverPlacement, SkeletonVariant, SelectChangeEvent, SelectInputEvent, SpinnerSize, SpinnerVariant, TextType, TypewriterType, };
|
94
|
+
export { Accordion, AccordionButton, AccordionGroup, AlertChip, Animation, Appheader, Avatar, AvatarButton, Badge, Brandvisual, Bullet, Button, ButtonGroup, ButtonLink, Card, CardButton, CardCheckbox, CardRadio, Checkbox, Chip, Coachmark, Dialog, Divider, FilterChip, FormfieldGroup, Icon, IconProvider, Input, InputChip, Link, LinkButton, Linksimple, List, Listheader, ListItem, Marker, MenuBar, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuPopover, MenuSection, NavMenuItem, OptGroup, Option, Password, Popover, Presence, Progressbar, Progressspinner, Radio, RadioGroup, ScreenreaderAnnouncer, Searchfield, Select, SelectListbox, SideNavigation, Skeleton, Spinner, StaticCheckbox, StaticRadio, StaticToggle, Stepper, StepperConnector, StepperItem, Tab, TabList, Text, Textarea, ThemeProvider, Toast, Toggle, Typewriter, ToggleTip, Tooltip, VirtualizedList, Combobox, Slider, ListBox, };
|
95
|
+
export type { BadgeType, ButtonColor, ButtonVariant, IconButtonSize, MenuPopoverActionEvent, MenuPopoverChangeEvent, MenuSectionChangeEvent, PillButtonSize, PopoverPlacement, SkeletonVariant, SelectChangeEvent, SelectInputEvent, SpinnerSize, SpinnerVariant, TextType, TypewriterType, InputInputEvent, InputChangeEvent, InputFocusEvent, InputBlurEvent, InputClearEvent, };
|
94
96
|
export { BUTTON_COLORS, BUTTON_VARIANTS, ICON_BUTTON_SIZES, inMemoryCache, PILL_BUTTON_SIZES, SKELETON_VARIANTS, webAPIIconsCache, };
|
package/dist/index.js
CHANGED
@@ -78,11 +78,12 @@ import Tooltip from './components/tooltip';
|
|
78
78
|
import VirtualizedList from './components/virtualizedlist';
|
79
79
|
import Combobox from './components/combobox';
|
80
80
|
import Slider from './components/slider';
|
81
|
+
import ListBox from './components/listbox';
|
81
82
|
// Constants / Utils Imports
|
82
83
|
import { BUTTON_COLORS, BUTTON_VARIANTS, ICON_BUTTON_SIZES, PILL_BUTTON_SIZES, } from './components/button/button.constants';
|
83
84
|
import { SKELETON_VARIANTS } from './components/skeleton/skeleton.constants';
|
84
85
|
import { inMemoryCache, webAPIIconsCache } from './utils/icon-cache';
|
85
86
|
// Components Exports
|
86
|
-
export { Accordion, AccordionButton, AccordionGroup, AlertChip, Animation, Appheader, Avatar, AvatarButton, Badge, Brandvisual, Bullet, Button, ButtonGroup, ButtonLink, Card, CardButton, CardCheckbox, CardRadio, Checkbox, Chip, Coachmark, Dialog, Divider, FilterChip, FormfieldGroup, Icon, IconProvider, Input, InputChip, Link, LinkButton, Linksimple, List, Listheader, ListItem, Marker, MenuBar, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuPopover, MenuSection, NavMenuItem, OptGroup, Option, Password, Popover, Presence, Progressbar, Progressspinner, Radio, RadioGroup, ScreenreaderAnnouncer, Searchfield, Select, SelectListbox, SideNavigation, Skeleton, Spinner, StaticCheckbox, StaticRadio, StaticToggle, Stepper, StepperConnector, StepperItem, Tab, TabList, Text, Textarea, ThemeProvider, Toast, Toggle, Typewriter, ToggleTip, Tooltip, VirtualizedList, Combobox, Slider, };
|
87
|
+
export { Accordion, AccordionButton, AccordionGroup, AlertChip, Animation, Appheader, Avatar, AvatarButton, Badge, Brandvisual, Bullet, Button, ButtonGroup, ButtonLink, Card, CardButton, CardCheckbox, CardRadio, Checkbox, Chip, Coachmark, Dialog, Divider, FilterChip, FormfieldGroup, Icon, IconProvider, Input, InputChip, Link, LinkButton, Linksimple, List, Listheader, ListItem, Marker, MenuBar, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuPopover, MenuSection, NavMenuItem, OptGroup, Option, Password, Popover, Presence, Progressbar, Progressspinner, Radio, RadioGroup, ScreenreaderAnnouncer, Searchfield, Select, SelectListbox, SideNavigation, Skeleton, Spinner, StaticCheckbox, StaticRadio, StaticToggle, Stepper, StepperConnector, StepperItem, Tab, TabList, Text, Textarea, ThemeProvider, Toast, Toggle, Typewriter, ToggleTip, Tooltip, VirtualizedList, Combobox, Slider, ListBox, };
|
87
88
|
// Constants / Utils Exports
|
88
89
|
export { BUTTON_COLORS, BUTTON_VARIANTS, ICON_BUTTON_SIZES, inMemoryCache, PILL_BUTTON_SIZES, SKELETON_VARIANTS, webAPIIconsCache, };
|
package/dist/react/index.d.ts
CHANGED
@@ -28,12 +28,13 @@ export { default as FormfieldGroup } from './formfieldgroup';
|
|
28
28
|
export { default as FormfieldWrapper } from './formfieldwrapper';
|
29
29
|
export { default as Icon } from './icon';
|
30
30
|
export { default as IconProvider } from './iconprovider';
|
31
|
-
export { default as Input } from './input';
|
32
31
|
export { default as InputChip } from './inputchip';
|
32
|
+
export { default as Input } from './input';
|
33
33
|
export { default as Link } from './link';
|
34
34
|
export { default as LinkButton } from './linkbutton';
|
35
35
|
export { default as Linksimple } from './linksimple';
|
36
36
|
export { default as List } from './list';
|
37
|
+
export { default as ListBox } from './listbox';
|
37
38
|
export { default as Listheader } from './listheader';
|
38
39
|
export { default as ListItem } from './listitem';
|
39
40
|
export { default as Marker } from './marker';
|
@@ -72,9 +73,9 @@ export { default as TabList } from './tablist';
|
|
72
73
|
export { default as Text } from './text';
|
73
74
|
export { default as Textarea } from './textarea';
|
74
75
|
export { default as ThemeProvider } from './themeprovider';
|
75
|
-
export { default as Toast } from './toast';
|
76
76
|
export { default as Toggle } from './toggle';
|
77
|
-
export { default as ToggleTip } from './toggletip';
|
78
77
|
export { default as Tooltip } from './tooltip';
|
78
|
+
export { default as Toast } from './toast';
|
79
|
+
export { default as ToggleTip } from './toggletip';
|
79
80
|
export { default as Typewriter } from './typewriter';
|
80
81
|
export { default as VirtualizedList } from './virtualizedlist';
|
package/dist/react/index.js
CHANGED
@@ -28,12 +28,13 @@ export { default as FormfieldGroup } from './formfieldgroup';
|
|
28
28
|
export { default as FormfieldWrapper } from './formfieldwrapper';
|
29
29
|
export { default as Icon } from './icon';
|
30
30
|
export { default as IconProvider } from './iconprovider';
|
31
|
-
export { default as Input } from './input';
|
32
31
|
export { default as InputChip } from './inputchip';
|
32
|
+
export { default as Input } from './input';
|
33
33
|
export { default as Link } from './link';
|
34
34
|
export { default as LinkButton } from './linkbutton';
|
35
35
|
export { default as Linksimple } from './linksimple';
|
36
36
|
export { default as List } from './list';
|
37
|
+
export { default as ListBox } from './listbox';
|
37
38
|
export { default as Listheader } from './listheader';
|
38
39
|
export { default as ListItem } from './listitem';
|
39
40
|
export { default as Marker } from './marker';
|
@@ -72,9 +73,9 @@ export { default as TabList } from './tablist';
|
|
72
73
|
export { default as Text } from './text';
|
73
74
|
export { default as Textarea } from './textarea';
|
74
75
|
export { default as ThemeProvider } from './themeprovider';
|
75
|
-
export { default as Toast } from './toast';
|
76
76
|
export { default as Toggle } from './toggle';
|
77
|
-
export { default as ToggleTip } from './toggletip';
|
78
77
|
export { default as Tooltip } from './tooltip';
|
78
|
+
export { default as Toast } from './toast';
|
79
|
+
export { default as ToggleTip } from './toggletip';
|
79
80
|
export { default as Typewriter } from './typewriter';
|
80
81
|
export { default as VirtualizedList } from './virtualizedlist';
|
@@ -45,9 +45,9 @@ import Component from '../../components/input';
|
|
45
45
|
*/
|
46
46
|
declare const reactWrapper: import("@lit/react").ReactWebComponent<Component, {
|
47
47
|
onClear: EventName<import("../../components/input/input.types").InputClearEvent>;
|
48
|
-
onInput: EventName<import("../../
|
48
|
+
onInput: EventName<import("../../components/input/input.types").InputInputEvent>;
|
49
49
|
onChange: EventName<import("../../components/input/input.types").InputChangeEvent>;
|
50
|
-
onFocus: EventName<import("../../
|
51
|
-
onBlur: EventName<import("../../
|
50
|
+
onFocus: EventName<import("../../components/input/input.types").InputFocusEvent>;
|
51
|
+
onBlur: EventName<import("../../components/input/input.types").InputBlurEvent>;
|
52
52
|
}>;
|
53
53
|
export default reactWrapper;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { type EventName } from '@lit/react';
|
2
|
+
import Component from '../../components/listbox';
|
3
|
+
/**
|
4
|
+
* listbox component presents a list of options and allows a user to select one of them.
|
5
|
+
*
|
6
|
+
* Notes:
|
7
|
+
* - This is a standalone listbox component. Select has its own mdc-selectlistbox component.
|
8
|
+
* - this component has name and value attributes and also emits change event,
|
9
|
+
* but it is not a form control (yet).
|
10
|
+
*
|
11
|
+
* @dependency mdc-list
|
12
|
+
* @dependency mdc-icon
|
13
|
+
* @dependency mdc-text
|
14
|
+
* @dependency mdc-option
|
15
|
+
* @dependency mdc-optgroup
|
16
|
+
*
|
17
|
+
* @tagname mdc-listbox
|
18
|
+
*
|
19
|
+
* @cssproperty --mdc-listbox-max-height - max height of the listbox
|
20
|
+
*
|
21
|
+
* @slot default - This is a default/unnamed slot, where options and optgroups are placed
|
22
|
+
*
|
23
|
+
* @csspart container - The container of the listbox
|
24
|
+
*
|
25
|
+
* @event change - (React: onChange) This event is emitted when the selected item changed
|
26
|
+
*/
|
27
|
+
declare const reactWrapper: import("@lit/react").ReactWebComponent<Component, {
|
28
|
+
onChange: EventName<import("../../components/listbox/listbox.types").ListBoxChangeEvent>;
|
29
|
+
}>;
|
30
|
+
export default reactWrapper;
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import * as React from 'react';
|
2
|
+
import { createComponent } from '@lit/react';
|
3
|
+
import Component from '../../components/listbox';
|
4
|
+
import { TAG_NAME } from '../../components/listbox/listbox.constants';
|
5
|
+
/**
|
6
|
+
* listbox component presents a list of options and allows a user to select one of them.
|
7
|
+
*
|
8
|
+
* Notes:
|
9
|
+
* - This is a standalone listbox component. Select has its own mdc-selectlistbox component.
|
10
|
+
* - this component has name and value attributes and also emits change event,
|
11
|
+
* but it is not a form control (yet).
|
12
|
+
*
|
13
|
+
* @dependency mdc-list
|
14
|
+
* @dependency mdc-icon
|
15
|
+
* @dependency mdc-text
|
16
|
+
* @dependency mdc-option
|
17
|
+
* @dependency mdc-optgroup
|
18
|
+
*
|
19
|
+
* @tagname mdc-listbox
|
20
|
+
*
|
21
|
+
* @cssproperty --mdc-listbox-max-height - max height of the listbox
|
22
|
+
*
|
23
|
+
* @slot default - This is a default/unnamed slot, where options and optgroups are placed
|
24
|
+
*
|
25
|
+
* @csspart container - The container of the listbox
|
26
|
+
*
|
27
|
+
* @event change - (React: onChange) This event is emitted when the selected item changed
|
28
|
+
*/
|
29
|
+
const reactWrapper = createComponent({
|
30
|
+
tagName: TAG_NAME,
|
31
|
+
elementClass: Component,
|
32
|
+
react: React,
|
33
|
+
events: {
|
34
|
+
onChange: 'change',
|
35
|
+
},
|
36
|
+
displayName: 'ListBox',
|
37
|
+
});
|
38
|
+
export default reactWrapper;
|
@@ -42,10 +42,10 @@ import Component from '../../components/password';
|
|
42
42
|
*
|
43
43
|
*/
|
44
44
|
declare const reactWrapper: import("@lit/react").ReactWebComponent<Component, {
|
45
|
-
onInput: EventName<import("../../
|
45
|
+
onInput: EventName<import("../../components/input/input.types").InputInputEvent>;
|
46
46
|
onChange: EventName<import("../../components/input/input.types").InputChangeEvent>;
|
47
|
-
onFocus: EventName<import("../../
|
48
|
-
onBlur: EventName<import("../../
|
47
|
+
onFocus: EventName<import("../../components/input/input.types").InputFocusEvent>;
|
48
|
+
onBlur: EventName<import("../../components/input/input.types").InputBlurEvent>;
|
49
49
|
onClear: EventName<import("../../components/input/input.types").InputClearEvent>;
|
50
50
|
}>;
|
51
51
|
export default reactWrapper;
|
@@ -18,10 +18,10 @@ import Component from '../../components/searchfield';
|
|
18
18
|
* @slot filters - Slot for input chips
|
19
19
|
*/
|
20
20
|
declare const reactWrapper: import("@lit/react").ReactWebComponent<Component, {
|
21
|
-
onInput: EventName<import("../../
|
21
|
+
onInput: EventName<import("../../components/input/input.types").InputInputEvent>;
|
22
22
|
onChange: EventName<import("../../components/input/input.types").InputChangeEvent>;
|
23
|
-
onFocus: EventName<import("../../
|
24
|
-
onBlur: EventName<import("../../
|
23
|
+
onFocus: EventName<import("../../components/input/input.types").InputFocusEvent>;
|
24
|
+
onBlur: EventName<import("../../components/input/input.types").InputBlurEvent>;
|
25
25
|
onClear: EventName<import("../../components/input/input.types").InputClearEvent>;
|
26
26
|
}>;
|
27
27
|
export default reactWrapper;
|
@@ -0,0 +1,153 @@
|
|
1
|
+
import { ReactiveController } from 'lit';
|
2
|
+
import type { Component } from '../../models';
|
3
|
+
export type ElementStoreChangeTypes = 'added' | 'removed';
|
4
|
+
interface ElementStoreOptions {
|
5
|
+
/**
|
6
|
+
* - A function to determine if an item is valid for caching.
|
7
|
+
*/
|
8
|
+
isValidItem: (item: any) => boolean;
|
9
|
+
}
|
10
|
+
/**
|
11
|
+
* ElementStore is a controller that manages a collection of elements.
|
12
|
+
*
|
13
|
+
* @example
|
14
|
+
* ```ts
|
15
|
+
* // Add and remove item based on the disabled state
|
16
|
+
* class Container extends Component {
|
17
|
+
* private this.store = new ElementStore<HTMLLIElement>(this, {
|
18
|
+
* validItemPredicate: (item) => item.matches('li:not([disabled])')
|
19
|
+
* });
|
20
|
+
*
|
21
|
+
* constructor() {
|
22
|
+
* super();
|
23
|
+
* this.addEventListener('modified', this.handleModifiedEvent);
|
24
|
+
* }
|
25
|
+
*
|
26
|
+
* handleModifiedEvent(event: LifeCycleModifiedEvent) {
|
27
|
+
* if (event.details.change === 'enabled') {
|
28
|
+
* this.store.addItemToCacheAt(event.target);
|
29
|
+
* } else if (event.details.change === 'disabled') {
|
30
|
+
* this.store.removeItemFromCache(event.target)
|
31
|
+
* }
|
32
|
+
* }
|
33
|
+
* }
|
34
|
+
* ```
|
35
|
+
*/
|
36
|
+
export declare class ElementStore<TItem extends HTMLElement> implements ReactiveController {
|
37
|
+
private host;
|
38
|
+
/**
|
39
|
+
* Checks if the item is valid.
|
40
|
+
* Invalid items will not be collected or processed.
|
41
|
+
* This method can be overridden by subclasses to define custom validation logic.
|
42
|
+
*
|
43
|
+
* @param item - The item to validate.
|
44
|
+
* @returns - True if the item is valid, false otherwise.
|
45
|
+
*/
|
46
|
+
private readonly isValidItem;
|
47
|
+
/** Stored items */
|
48
|
+
private cache;
|
49
|
+
/** Access to stored items */
|
50
|
+
get items(): TItem[];
|
51
|
+
/**
|
52
|
+
* Creates an instance of ElementStore.
|
53
|
+
* This controller manages a collection of elements, allowing for
|
54
|
+
* adding and removing items based on lifecycle events.
|
55
|
+
*
|
56
|
+
* A component can have multiple ElementStore controllers
|
57
|
+
*
|
58
|
+
* All ElementStore must be created before any `addEventListener` in the constructor, see the example.
|
59
|
+
*
|
60
|
+
* Note: This controller relies on `created` and `destroyed` events.
|
61
|
+
* For the best result, dispatch these events on the child components or use the `LifeCycleMixin` mixin.
|
62
|
+
* And make sure the `destroyed` event properly propagated to the host, `CaptureDestroyEventForChildElement` mixin
|
63
|
+
* can help with that.
|
64
|
+
*
|
65
|
+
* @example
|
66
|
+
* ```ts
|
67
|
+
* class Container extends CaptureDestroyEventForChildElement(Component) {
|
68
|
+
* private itemsStore = new ElementStore<Item>(this, {
|
69
|
+
* isValidItem: this.isValidItem,
|
70
|
+
* });
|
71
|
+
*
|
72
|
+
* constructor() {
|
73
|
+
* super()
|
74
|
+
* this.addEventListener('modified', this.handleModifiedEvent);
|
75
|
+
* }
|
76
|
+
*
|
77
|
+
* private handleModifiedEvent(event: LifeCycleModifiedEvent) {
|
78
|
+
* const item = event.target as Item;
|
79
|
+
*
|
80
|
+
* switch (event.detail.change) {
|
81
|
+
* case 'enabled': return this.itemsStore.add(item);
|
82
|
+
* case 'disabled': return this.itemsStore.delete(item);
|
83
|
+
* }
|
84
|
+
* }
|
85
|
+
*
|
86
|
+
* private isValidItem(item: Element): boolean {
|
87
|
+
* return item.matches(`${ITEM_TAGNAME}:not([disabled])`);
|
88
|
+
* }
|
89
|
+
* ```
|
90
|
+
*
|
91
|
+
* @param host - The host component that this controller is attached to.
|
92
|
+
* @param options - Element store options
|
93
|
+
*/
|
94
|
+
constructor(host: Component, options?: ElementStoreOptions);
|
95
|
+
hostConnected(): void;
|
96
|
+
hostDisconnected(): void;
|
97
|
+
/**
|
98
|
+
* Handles the item creation event.
|
99
|
+
*
|
100
|
+
* @param event - The event triggered when an item is created.
|
101
|
+
*/
|
102
|
+
protected itemCreationHandler: (event: Event) => void;
|
103
|
+
/**
|
104
|
+
* Handles the item destroy event.
|
105
|
+
*
|
106
|
+
* @param event - The event triggered when an item is destroyed.
|
107
|
+
*/
|
108
|
+
protected itemDestroyHandler: (event: Event) => void;
|
109
|
+
/**
|
110
|
+
* Adds an item to the cache.
|
111
|
+
* If the item is valid and not already in the cache, it will be added at the end of the cache.
|
112
|
+
*
|
113
|
+
* It uses the `compareDocumentPosition` method to determine the correct order of the items in the cache.
|
114
|
+
* @see [compareDocumentPosition](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition)
|
115
|
+
*
|
116
|
+
* @param newItem - The item to add to the cache.
|
117
|
+
*/
|
118
|
+
add(newItem: Element): void;
|
119
|
+
/**
|
120
|
+
* Adds an item to the cache.
|
121
|
+
* If the item is valid and not already in the cache, it will be added to the specified index.
|
122
|
+
*
|
123
|
+
* @param newItem - The item to add to the cache.
|
124
|
+
* @param index - The index at which to add the item.
|
125
|
+
*/
|
126
|
+
addAt(newItem: Element, index: number): void;
|
127
|
+
/**
|
128
|
+
* Adds an item to the cache at the specified index.
|
129
|
+
* When the index
|
130
|
+
* is `undefined`, the item is added automatically keeping the DOM order.
|
131
|
+
* is `-1`, the item is added to the end of the cache.
|
132
|
+
* is `>= 0`, the item is added at that index.
|
133
|
+
* otherwise, do nothing.
|
134
|
+
*
|
135
|
+
* @param item - The item to add to the cache.
|
136
|
+
* @param index - The index at which to add the item. If `undefined`, the item is added automatically.
|
137
|
+
*/
|
138
|
+
private addItem;
|
139
|
+
/**
|
140
|
+
* Removes an item from the cache.
|
141
|
+
*
|
142
|
+
* @param item - The item to remove from the cache.
|
143
|
+
*/
|
144
|
+
delete(item: Element): void;
|
145
|
+
/**
|
146
|
+
* Sets the item cache to the provided items.
|
147
|
+
* If no items are provided, it clears the cache.
|
148
|
+
*
|
149
|
+
* @param items - The items to set in the cache.
|
150
|
+
*/
|
151
|
+
protected reset(items: TItem[]): void;
|
152
|
+
}
|
153
|
+
export {};
|
@@ -0,0 +1,171 @@
|
|
1
|
+
import { LIFE_CYCLE_EVENTS } from '../mixins/lifecycle/lifecycle.contants';
|
2
|
+
import { isBefore } from '../dom';
|
3
|
+
const defaultIsValidFn = (item) => !!item;
|
4
|
+
/**
|
5
|
+
* ElementStore is a controller that manages a collection of elements.
|
6
|
+
*
|
7
|
+
* @example
|
8
|
+
* ```ts
|
9
|
+
* // Add and remove item based on the disabled state
|
10
|
+
* class Container extends Component {
|
11
|
+
* private this.store = new ElementStore<HTMLLIElement>(this, {
|
12
|
+
* validItemPredicate: (item) => item.matches('li:not([disabled])')
|
13
|
+
* });
|
14
|
+
*
|
15
|
+
* constructor() {
|
16
|
+
* super();
|
17
|
+
* this.addEventListener('modified', this.handleModifiedEvent);
|
18
|
+
* }
|
19
|
+
*
|
20
|
+
* handleModifiedEvent(event: LifeCycleModifiedEvent) {
|
21
|
+
* if (event.details.change === 'enabled') {
|
22
|
+
* this.store.addItemToCacheAt(event.target);
|
23
|
+
* } else if (event.details.change === 'disabled') {
|
24
|
+
* this.store.removeItemFromCache(event.target)
|
25
|
+
* }
|
26
|
+
* }
|
27
|
+
* }
|
28
|
+
* ```
|
29
|
+
*/
|
30
|
+
export class ElementStore {
|
31
|
+
/** Access to stored items */
|
32
|
+
get items() {
|
33
|
+
return this.cache;
|
34
|
+
}
|
35
|
+
/**
|
36
|
+
* Creates an instance of ElementStore.
|
37
|
+
* This controller manages a collection of elements, allowing for
|
38
|
+
* adding and removing items based on lifecycle events.
|
39
|
+
*
|
40
|
+
* A component can have multiple ElementStore controllers
|
41
|
+
*
|
42
|
+
* All ElementStore must be created before any `addEventListener` in the constructor, see the example.
|
43
|
+
*
|
44
|
+
* Note: This controller relies on `created` and `destroyed` events.
|
45
|
+
* For the best result, dispatch these events on the child components or use the `LifeCycleMixin` mixin.
|
46
|
+
* And make sure the `destroyed` event properly propagated to the host, `CaptureDestroyEventForChildElement` mixin
|
47
|
+
* can help with that.
|
48
|
+
*
|
49
|
+
* @example
|
50
|
+
* ```ts
|
51
|
+
* class Container extends CaptureDestroyEventForChildElement(Component) {
|
52
|
+
* private itemsStore = new ElementStore<Item>(this, {
|
53
|
+
* isValidItem: this.isValidItem,
|
54
|
+
* });
|
55
|
+
*
|
56
|
+
* constructor() {
|
57
|
+
* super()
|
58
|
+
* this.addEventListener('modified', this.handleModifiedEvent);
|
59
|
+
* }
|
60
|
+
*
|
61
|
+
* private handleModifiedEvent(event: LifeCycleModifiedEvent) {
|
62
|
+
* const item = event.target as Item;
|
63
|
+
*
|
64
|
+
* switch (event.detail.change) {
|
65
|
+
* case 'enabled': return this.itemsStore.add(item);
|
66
|
+
* case 'disabled': return this.itemsStore.delete(item);
|
67
|
+
* }
|
68
|
+
* }
|
69
|
+
*
|
70
|
+
* private isValidItem(item: Element): boolean {
|
71
|
+
* return item.matches(`${ITEM_TAGNAME}:not([disabled])`);
|
72
|
+
* }
|
73
|
+
* ```
|
74
|
+
*
|
75
|
+
* @param host - The host component that this controller is attached to.
|
76
|
+
* @param options - Element store options
|
77
|
+
*/
|
78
|
+
constructor(host, options) {
|
79
|
+
/** Stored items */
|
80
|
+
this.cache = [];
|
81
|
+
/**
|
82
|
+
* Handles the item creation event.
|
83
|
+
*
|
84
|
+
* @param event - The event triggered when an item is created.
|
85
|
+
*/
|
86
|
+
this.itemCreationHandler = (event) => {
|
87
|
+
this.addItem(event.target, undefined);
|
88
|
+
};
|
89
|
+
/**
|
90
|
+
* Handles the item destroy event.
|
91
|
+
*
|
92
|
+
* @param event - The event triggered when an item is destroyed.
|
93
|
+
*/
|
94
|
+
this.itemDestroyHandler = (event) => {
|
95
|
+
this.delete(event.target);
|
96
|
+
};
|
97
|
+
this.host = host;
|
98
|
+
host.addController(this);
|
99
|
+
this.isValidItem = (options === null || options === void 0 ? void 0 : options.isValidItem) || defaultIsValidFn;
|
100
|
+
this.host.addEventListener(LIFE_CYCLE_EVENTS.CREATED, this.itemCreationHandler);
|
101
|
+
this.host.addEventListener(LIFE_CYCLE_EVENTS.DESTROYED, this.itemDestroyHandler);
|
102
|
+
}
|
103
|
+
hostConnected() { }
|
104
|
+
hostDisconnected() { }
|
105
|
+
/**
|
106
|
+
* Adds an item to the cache.
|
107
|
+
* If the item is valid and not already in the cache, it will be added at the end of the cache.
|
108
|
+
*
|
109
|
+
* It uses the `compareDocumentPosition` method to determine the correct order of the items in the cache.
|
110
|
+
* @see [compareDocumentPosition](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition)
|
111
|
+
*
|
112
|
+
* @param newItem - The item to add to the cache.
|
113
|
+
*/
|
114
|
+
add(newItem) {
|
115
|
+
this.addItem(newItem, undefined);
|
116
|
+
}
|
117
|
+
/**
|
118
|
+
* Adds an item to the cache.
|
119
|
+
* If the item is valid and not already in the cache, it will be added to the specified index.
|
120
|
+
*
|
121
|
+
* @param newItem - The item to add to the cache.
|
122
|
+
* @param index - The index at which to add the item.
|
123
|
+
*/
|
124
|
+
addAt(newItem, index) {
|
125
|
+
this.addItem(newItem, index);
|
126
|
+
}
|
127
|
+
/**
|
128
|
+
* Adds an item to the cache at the specified index.
|
129
|
+
* When the index
|
130
|
+
* is `undefined`, the item is added automatically keeping the DOM order.
|
131
|
+
* is `-1`, the item is added to the end of the cache.
|
132
|
+
* is `>= 0`, the item is added at that index.
|
133
|
+
* otherwise, do nothing.
|
134
|
+
*
|
135
|
+
* @param item - The item to add to the cache.
|
136
|
+
* @param index - The index at which to add the item. If `undefined`, the item is added automatically.
|
137
|
+
*/
|
138
|
+
addItem(item, index = undefined) {
|
139
|
+
const newItem = item;
|
140
|
+
if (this.isValidItem(newItem) && !this.cache.includes(newItem)) {
|
141
|
+
const idx = index === undefined ? this.cache.findIndex(e => isBefore(newItem, e)) : index;
|
142
|
+
if (idx === -1) {
|
143
|
+
this.cache.push(newItem);
|
144
|
+
}
|
145
|
+
else if (idx >= 0) {
|
146
|
+
this.cache.splice(idx, 0, newItem);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
/**
|
151
|
+
* Removes an item from the cache.
|
152
|
+
*
|
153
|
+
* @param item - The item to remove from the cache.
|
154
|
+
*/
|
155
|
+
delete(item) {
|
156
|
+
const idx = this.cache.indexOf(item);
|
157
|
+
if (idx !== -1) {
|
158
|
+
this.cache.splice(idx, 1);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
/**
|
162
|
+
* Sets the item cache to the provided items.
|
163
|
+
* If no items are provided, it clears the cache.
|
164
|
+
*
|
165
|
+
* @param items - The items to set in the cache.
|
166
|
+
*/
|
167
|
+
reset(items) {
|
168
|
+
this.cache.length = 0;
|
169
|
+
this.cache.push(...(items || []));
|
170
|
+
}
|
171
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/**
|
2
|
+
* nodeB follows nodeA in either a pre-order depth-first traversal of a tree containing both
|
3
|
+
* (e.g., as a descendant or following sibling or a descendant of a following sibling or
|
4
|
+
* following sibling of an ancestor) or (if they are disconnected) in an arbitrary but
|
5
|
+
* consistent ordering.
|
6
|
+
*
|
7
|
+
* @param nodeA - The first node to compare.
|
8
|
+
* @param nodeB - The second node to compare.
|
9
|
+
* @returns - True if nodeA is before nodeB, false otherwise.
|
10
|
+
*
|
11
|
+
* @see [compareDocumentPosition](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition)
|
12
|
+
*/
|
13
|
+
export declare const isBefore: (nodeA: Element, nodeB: Element) => boolean;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
/* eslint-disable max-classes-per-file,no-bitwise */
|
2
|
+
/**
|
3
|
+
* nodeB follows nodeA in either a pre-order depth-first traversal of a tree containing both
|
4
|
+
* (e.g., as a descendant or following sibling or a descendant of a following sibling or
|
5
|
+
* following sibling of an ancestor) or (if they are disconnected) in an arbitrary but
|
6
|
+
* consistent ordering.
|
7
|
+
*
|
8
|
+
* @param nodeA - The first node to compare.
|
9
|
+
* @param nodeB - The second node to compare.
|
10
|
+
* @returns - True if nodeA is before nodeB, false otherwise.
|
11
|
+
*
|
12
|
+
* @see [compareDocumentPosition](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition)
|
13
|
+
*/
|
14
|
+
export const isBefore = (nodeA, nodeB) => !!(nodeA.compareDocumentPosition(nodeB) & Node.DOCUMENT_POSITION_FOLLOWING);
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import type { LitElement } from 'lit';
|
2
|
+
import type { Constructor } from './index.types';
|
3
|
+
export declare class ItemCollectionMixinInterface<TItem> {
|
4
|
+
/**
|
5
|
+
* List of items (cached)
|
6
|
+
*/
|
7
|
+
get items(): TItem[];
|
8
|
+
/**
|
9
|
+
* Checks if the item is valid.
|
10
|
+
* Invalid items will not be collected or processed.
|
11
|
+
* This method can be overridden by subclasses to define custom validation logic.
|
12
|
+
*
|
13
|
+
* @param item - The item to validate.
|
14
|
+
* @returns - True if the item is valid, false otherwise.
|
15
|
+
*/
|
16
|
+
protected isValidItem(item: Element): boolean;
|
17
|
+
/**
|
18
|
+
* Adds an item to the cache at the specified index.
|
19
|
+
* When the index
|
20
|
+
* is `undefined`, the item is added automatically keeping the DOM order.
|
21
|
+
* is `-1`, the item is added to the end of the cache.
|
22
|
+
* is `>= 0`, the item is added at that index.
|
23
|
+
* otherwise, do nothing.
|
24
|
+
*
|
25
|
+
* @param item - The item to add to the cache.
|
26
|
+
* @param index - The index at which to add the item. If -1, adds to the end.
|
27
|
+
*/
|
28
|
+
protected addItemToCacheAt(item: Element, index?: number): void;
|
29
|
+
/**
|
30
|
+
* Removes an item from the cache.
|
31
|
+
*
|
32
|
+
* @param item - The item to remove from the cache.
|
33
|
+
*/
|
34
|
+
protected removeItemFromCache(item: Element): void;
|
35
|
+
/**
|
36
|
+
* Sets the item cache to the provided items.
|
37
|
+
* If no items are provided, it clears the cache.
|
38
|
+
*
|
39
|
+
* @param items - The items to set in the cache.
|
40
|
+
*/
|
41
|
+
protected setItemCache(items?: TItem[]): void;
|
42
|
+
}
|
43
|
+
/**
|
44
|
+
* This mixin collects and cache items based on the `created` and `destroyed` lifecycle events.
|
45
|
+
* Also provides methods to manage the item cache.
|
46
|
+
*
|
47
|
+
* @example
|
48
|
+
* ```ts
|
49
|
+
* // Add and remove item based on the disabled state
|
50
|
+
* class ListBox extends ItemCollectionMixin<Option, typeof Component>(Component) {
|
51
|
+
* constructor() {
|
52
|
+
* super();
|
53
|
+
* this.addEventListener('modified', this.handleModifiedEvent);
|
54
|
+
* }
|
55
|
+
*
|
56
|
+
* handleModifiedEvent(event: LifeCycleModifiedEvent) {
|
57
|
+
* if (event.details.change === 'enabled') {
|
58
|
+
* this.addItemToCacheAt(event.target);
|
59
|
+
* } else if (event.details.change === 'disabled') {
|
60
|
+
* this.removeItemFromCache(event.target)
|
61
|
+
* }
|
62
|
+
* }
|
63
|
+
* }
|
64
|
+
* ```
|
65
|
+
*
|
66
|
+
* @param superClass - The class to extend with the mixin.
|
67
|
+
*/
|
68
|
+
export declare const ItemCollectionMixin: <TItem extends HTMLElement, T extends Constructor<LitElement>>(superClass: T) => Constructor<ItemCollectionMixinInterface<TItem>> & T;
|