@snack-uikit/list 0.11.1-preview-5d3667ec.0 → 0.11.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.
Files changed (125) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +22 -61
  3. package/dist/components/Items/AccordionItem/AccordionItem.d.ts +4 -2
  4. package/dist/components/Items/AccordionItem/AccordionItem.js +18 -16
  5. package/dist/components/Items/BaseItem/BaseItem.d.ts +8 -4
  6. package/dist/components/Items/BaseItem/BaseItem.js +20 -14
  7. package/dist/components/Items/BaseItem/styles.module.css +20 -119
  8. package/dist/components/Items/GroupItem/GroupItem.d.ts +5 -1
  9. package/dist/components/Items/GroupItem/GroupItem.js +1 -3
  10. package/dist/components/Items/GroupSelectItem/GroupSelectItem.d.ts +4 -0
  11. package/dist/components/Items/GroupSelectItem/GroupSelectItem.js +18 -0
  12. package/dist/components/Items/GroupSelectItem/index.d.ts +1 -0
  13. package/dist/components/Items/GroupSelectItem/index.js +1 -0
  14. package/dist/components/Items/NextListItem/NextListItem.d.ts +6 -2
  15. package/dist/components/Items/NextListItem/NextListItem.js +55 -45
  16. package/dist/components/Items/NextListItem/constants.d.ts +2 -0
  17. package/dist/components/Items/NextListItem/constants.js +10 -0
  18. package/dist/components/Items/PinGroupItem/PinGroupItem.js +7 -3
  19. package/dist/components/Items/PinGroupItem/styles.module.css +26 -14
  20. package/dist/components/Items/SearchItem/SearchItem.d.ts +3 -1
  21. package/dist/components/Items/SearchItem/SearchItem.js +5 -8
  22. package/dist/components/Items/hooks.d.ts +11 -10
  23. package/dist/components/Items/hooks.js +64 -46
  24. package/dist/components/Items/index.d.ts +1 -5
  25. package/dist/components/Items/index.js +1 -5
  26. package/dist/components/Items/styles.module.css +33 -26
  27. package/dist/components/Items/types.d.ts +68 -34
  28. package/dist/components/Items/utils.d.ts +34 -16
  29. package/dist/components/Items/utils.js +135 -37
  30. package/dist/components/Lists/Droplist/DropList.d.ts +1 -1
  31. package/dist/components/Lists/Droplist/DropList.js +78 -50
  32. package/dist/components/Lists/List/List.d.ts +5 -9
  33. package/dist/components/Lists/List/List.js +61 -54
  34. package/dist/components/Lists/ListPrivate/ListPrivate.d.ts +6 -20
  35. package/dist/components/Lists/ListPrivate/ListPrivate.js +9 -8
  36. package/dist/components/Lists/ListPrivate/styles.module.css +1 -0
  37. package/dist/components/Lists/contexts/CollapseProvider.d.ts +13 -1
  38. package/dist/components/Lists/contexts/CollapseProvider.js +2 -0
  39. package/dist/components/Lists/contexts/FocusListProvider.d.ts +9 -0
  40. package/dist/components/Lists/contexts/FocusListProvider.js +5 -0
  41. package/dist/components/Lists/contexts/NewListProvider.d.ts +32 -0
  42. package/dist/components/Lists/contexts/{ListProvider.js → NewListProvider.js} +10 -5
  43. package/dist/components/Lists/contexts/SelectionProvider.d.ts +25 -15
  44. package/dist/components/Lists/contexts/SelectionProvider.js +17 -13
  45. package/dist/components/Lists/contexts/index.d.ts +3 -2
  46. package/dist/components/Lists/contexts/index.js +3 -2
  47. package/dist/components/Lists/hooks.d.ts +10 -12
  48. package/dist/components/Lists/hooks.js +73 -40
  49. package/dist/components/Lists/index.d.ts +2 -1
  50. package/dist/components/Lists/index.js +1 -0
  51. package/dist/components/Lists/types.d.ts +12 -14
  52. package/dist/components/index.d.ts +2 -2
  53. package/dist/components/index.js +1 -1
  54. package/dist/helperComponents/ItemContent/ItemContent.d.ts +15 -0
  55. package/dist/helperComponents/ItemContent/ItemContent.js +23 -0
  56. package/dist/helperComponents/ItemContent/index.d.ts +1 -0
  57. package/dist/helperComponents/ItemContent/index.js +1 -0
  58. package/dist/helperComponents/ItemContent/styles.module.css +117 -0
  59. package/dist/helperComponents/ListEmptyState/ListEmptyState.d.ts +2 -2
  60. package/dist/helperComponents/ListEmptyState/ListEmptyState.js +13 -4
  61. package/dist/helperComponents/Separator/Separator.d.ts +8 -2
  62. package/dist/helperComponents/Separator/Separator.js +22 -2
  63. package/dist/helperComponents/Separator/constants.d.ts +2 -0
  64. package/dist/helperComponents/Separator/constants.js +5 -0
  65. package/dist/helperComponents/Separator/styles.module.css +20 -1
  66. package/dist/helperComponents/index.d.ts +1 -0
  67. package/dist/helperComponents/index.js +1 -0
  68. package/dist/index.d.ts +2 -2
  69. package/dist/index.js +2 -2
  70. package/dist/types.d.ts +1 -1
  71. package/package.json +13 -18
  72. package/src/components/Items/AccordionItem/AccordionItem.tsx +24 -20
  73. package/src/components/Items/BaseItem/BaseItem.tsx +91 -82
  74. package/src/components/Items/BaseItem/styles.module.scss +19 -77
  75. package/src/components/Items/GroupItem/GroupItem.tsx +5 -5
  76. package/src/components/Items/GroupSelectItem/GroupSelectItem.tsx +44 -0
  77. package/src/components/Items/GroupSelectItem/index.ts +1 -0
  78. package/src/components/Items/NextListItem/NextListItem.tsx +88 -85
  79. package/src/components/Items/NextListItem/constants.ts +12 -0
  80. package/src/components/Items/PinGroupItem/PinGroupItem.tsx +14 -4
  81. package/src/components/Items/PinGroupItem/styles.module.scss +14 -14
  82. package/src/components/Items/SearchItem/SearchItem.tsx +6 -8
  83. package/src/components/Items/hooks.tsx +86 -66
  84. package/src/components/Items/index.ts +1 -5
  85. package/src/components/Items/styles.module.scss +36 -25
  86. package/src/components/Items/types.ts +91 -42
  87. package/src/components/Items/utils.ts +199 -52
  88. package/src/components/Lists/Droplist/DropList.tsx +155 -98
  89. package/src/components/Lists/List/List.tsx +129 -104
  90. package/src/components/Lists/ListPrivate/ListPrivate.tsx +19 -20
  91. package/src/components/Lists/ListPrivate/styles.module.scss +1 -0
  92. package/src/components/Lists/contexts/CollapseProvider.tsx +22 -0
  93. package/src/components/Lists/contexts/FocusListProvider.tsx +15 -0
  94. package/src/components/Lists/contexts/NewListProvider.tsx +54 -0
  95. package/src/components/Lists/contexts/SelectionProvider.tsx +56 -39
  96. package/src/components/Lists/contexts/index.ts +3 -2
  97. package/src/components/Lists/hooks.ts +124 -75
  98. package/src/components/Lists/index.ts +2 -2
  99. package/src/components/Lists/types.ts +20 -24
  100. package/src/components/index.ts +17 -2
  101. package/src/helperComponents/ItemContent/ItemContent.tsx +63 -0
  102. package/src/helperComponents/ItemContent/index.ts +1 -0
  103. package/src/helperComponents/ItemContent/styles.module.scss +78 -0
  104. package/src/helperComponents/ListEmptyState/ListEmptyState.tsx +26 -12
  105. package/src/helperComponents/Separator/Separator.tsx +49 -3
  106. package/src/helperComponents/Separator/constants.ts +7 -0
  107. package/src/helperComponents/Separator/styles.module.scss +41 -1
  108. package/src/helperComponents/index.ts +1 -0
  109. package/src/index.ts +4 -2
  110. package/src/types.ts +1 -1
  111. package/dist/components/Items/NextListItem/hooks.d.ts +0 -19
  112. package/dist/components/Items/NextListItem/hooks.js +0 -80
  113. package/dist/components/Lists/contexts/ListProvider.d.ts +0 -17
  114. package/dist/components/Lists/contexts/ParentListProvider.d.ts +0 -18
  115. package/dist/components/Lists/contexts/ParentListProvider.js +0 -12
  116. package/dist/hooks.d.ts +0 -5
  117. package/dist/hooks.js +0 -15
  118. package/dist/utils.d.ts +0 -30
  119. package/dist/utils.js +0 -115
  120. package/src/components/Items/NextListItem/hooks.ts +0 -105
  121. package/src/components/Lists/contexts/CollapseProvider.ts +0 -9
  122. package/src/components/Lists/contexts/ListProvider.tsx +0 -28
  123. package/src/components/Lists/contexts/ParentListProvider.tsx +0 -35
  124. package/src/hooks.ts +0 -27
  125. package/src/utils.ts +0 -175
@@ -1,122 +1,118 @@
1
- import { useCallback, useMemo, useRef, useState } from 'react';
1
+ import { KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
 
3
- import { Dropdown, DropdownProps } from '@snack-uikit/dropdown';
3
+ import { Dropdown } from '@snack-uikit/dropdown';
4
4
  import { ChevronRightSVG } from '@snack-uikit/icons';
5
5
 
6
- import { withCollapsedItems } from '../../../utils';
7
- import { ParentListContext, useParentListContext } from '../../Lists/contexts';
6
+ import { useCollapseContext, useFocusListContext, useNewListContext, useSelectionContext } from '../../Lists/contexts';
8
7
  import { ListPrivate } from '../../Lists/ListPrivate';
9
8
  import { BaseItem } from '../BaseItem';
10
9
  import { useGroupItemSelection } from '../hooks';
11
- import { NextListItemProps } from '../types';
12
- import { useKeyboardNavigation } from './hooks';
13
-
14
- const FALLBACK_PLACEMENTS: DropdownProps['fallbackPlacements'] = [
15
- 'right',
16
- 'right-start',
17
- 'right-end',
18
- 'left',
19
- 'left-start',
20
- 'left-end',
21
- ];
10
+ import { CommonFlattenProps, FlattenNextListItem, ItemId } from '../types';
11
+ import { extractActiveItems, isNextListItem } from '../utils';
12
+ import { FALLBACK_PLACEMENTS } from './constants';
13
+
14
+ type NextListItemProps = Omit<FlattenNextListItem, 'type'> & CommonFlattenProps & { focusId?: ItemId };
22
15
 
23
16
  export function NextListItem({
24
- items: itemsProp,
17
+ items,
25
18
  placement = 'right-start',
26
19
  id,
27
- search,
28
20
  scroll,
29
21
  scrollRef,
30
22
  disabled,
31
23
  onSublistOpenChanged,
24
+ allChildIds,
32
25
  loading = false,
26
+ focusId = id,
33
27
  ...option
34
28
  }: NextListItemProps) {
35
- const listRef = useRef<HTMLUListElement>(null);
29
+ const { flattenItems, focusFlattenItems } = useNewListContext();
30
+ const { isSelectionMultiple } = useSelectionContext();
31
+ const { openCollapseItems = [] } = useCollapseContext();
36
32
 
37
- const [openCollapsedItems, setOpenCollapsedItems] = useState<Array<number | string>>([]);
33
+ const item = flattenItems[id];
38
34
 
39
- const { items, itemRefs, ids, expandedIds } = useMemo(
40
- () =>
41
- withCollapsedItems({
42
- items: itemsProp,
43
- openCollapsedItems,
44
- }),
45
- [itemsProp, openCollapsedItems],
46
- );
35
+ const { ids, expandedIds } = useMemo(() => {
36
+ const { ids, expandedIds } = extractActiveItems({
37
+ focusCloseChildIds: items,
38
+ focusFlattenItems,
39
+ openCollapseItems,
40
+ isSelectionMultiple,
41
+ });
42
+
43
+ return { ids, expandedIds: expandedIds.concat([id]) };
44
+ }, [focusFlattenItems, id, isSelectionMultiple, items, openCollapseItems]);
45
+
46
+ const { handleListKeyDownFactory, activeItemId, forceUpdateActiveItemId } = useFocusListContext();
47
+
48
+ const [open, setOpen] = useState<boolean>();
49
+
50
+ const handleListKeyDown = useCallback(
51
+ (e: KeyboardEvent<HTMLElement>) => {
52
+ handleListKeyDownFactory(ids, expandedIds)(e);
53
+
54
+ if (e.key === 'ArrowLeft') {
55
+ forceUpdateActiveItemId?.(focusId);
56
+ setOpen(false);
57
+ e.stopPropagation();
47
58
 
48
- const { parentOpenNestedIndex, parentIds, triggerRef, parentResetNestedIndex, parentResetActiveFocusIndex } =
49
- useParentListContext();
50
-
51
- const {
52
- open,
53
- setOpen,
54
- handleListKeyDown,
55
- activeFocusIndex,
56
- openNestedIndex,
57
- resetNestedIndex,
58
- resetActiveFocusIndex,
59
- } = useKeyboardNavigation({ ids, expandedIds, itemRefs, id });
60
-
61
- const { isIndeterminate, checked, handleOnSelect } = useGroupItemSelection({ items: itemsProp, id, disabled });
62
-
63
- const handleOpenChange = useCallback(
64
- (open: boolean) => {
65
- if (!open) {
66
- resetActiveFocusIndex();
67
- resetNestedIndex();
68
- setOpenCollapsedItems([]);
59
+ return;
69
60
  }
70
- onSublistOpenChanged?.(open, id);
71
- setOpen(open);
72
61
  },
73
- [id, onSublistOpenChanged, resetActiveFocusIndex, resetNestedIndex, setOpen],
62
+ [handleListKeyDownFactory, ids, expandedIds, forceUpdateActiveItemId, focusId],
74
63
  );
75
64
 
76
- const handleOutsideClick = useCallback(() => {
77
- parentResetNestedIndex?.();
78
- parentResetActiveFocusIndex?.();
65
+ const { indeterminate, checked, handleOnSelect } = useGroupItemSelection({
66
+ items: isNextListItem<FlattenNextListItem>(item) ? item.items : [],
67
+ id,
68
+ disabled,
69
+ allChildIds,
70
+ });
79
71
 
72
+ const handleOutsideClick = useCallback(() => {
73
+ forceUpdateActiveItemId?.('~drop-focus');
74
+ setOpen(false);
80
75
  return true;
81
- }, [parentResetActiveFocusIndex, parentResetNestedIndex]);
76
+ }, [forceUpdateActiveItemId]);
77
+
78
+ const isOpen = useMemo(
79
+ () => Boolean(!disabled && activeItemId && focusFlattenItems[focusId].allChildIds.includes(activeItemId)),
80
+ [activeItemId, disabled, focusFlattenItems, focusId],
81
+ );
82
+
83
+ useEffect(() => {
84
+ setOpen(open => open && isOpen);
85
+ }, [id, isOpen]);
86
+
87
+ const listRef = useRef<HTMLElement>(null);
82
88
 
83
89
  return (
84
90
  <Dropdown
85
91
  outsideClick={handleOutsideClick}
86
92
  fallbackPlacements={FALLBACK_PLACEMENTS}
87
93
  content={
88
- <ParentListContext.Provider
89
- value={{
90
- parentIds: ids,
91
- parentActiveFocusIndex: activeFocusIndex,
92
- parentExpandedIds: expandedIds,
93
- parentItemRefs: itemRefs,
94
- parentOpenNestedIndex: openNestedIndex,
95
- triggerRef,
96
- parentRef: listRef,
97
- parentResetNestedIndex: resetNestedIndex,
98
- parentResetActiveFocusIndex: resetActiveFocusIndex,
99
- toggleOpenCollapsedItems: id =>
100
- setOpenCollapsedItems(items =>
101
- items.includes(id) ? items.filter(item => item !== id) : items.concat([id]),
102
- ),
94
+ <ListPrivate
95
+ onKeyDown={handleListKeyDown}
96
+ items={items}
97
+ nested
98
+ scroll={scroll}
99
+ tabIndex={0}
100
+ ref={listRef}
101
+ onFocus={e => {
102
+ e.stopPropagation();
103
+ forceUpdateActiveItemId?.(ids[0]);
103
104
  }}
104
- >
105
- <ListPrivate
106
- onKeyDown={handleListKeyDown}
107
- items={items}
108
- nested
109
- search={search}
110
- scroll={scroll}
111
- scrollRef={scrollRef}
112
- limitedScrollHeight
113
- loading={loading}
114
- />
115
- </ParentListContext.Provider>
105
+ scrollRef={scrollRef}
106
+ limitedScrollHeight
107
+ loading={loading}
108
+ />
116
109
  }
117
110
  trigger='hover'
118
- open={(open || parentIds[parentOpenNestedIndex] === id) && !disabled}
119
- onOpenChange={handleOpenChange}
111
+ open={isOpen || open}
112
+ onOpenChange={value => {
113
+ setOpen(value);
114
+ onSublistOpenChanged?.(value, id);
115
+ }}
120
116
  placement={placement}
121
117
  widthStrategy='auto'
122
118
  >
@@ -127,7 +123,14 @@ export function NextListItem({
127
123
  expandIcon={<ChevronRightSVG />}
128
124
  id={id}
129
125
  isParentNode
130
- indeterminate={isIndeterminate && !checked}
126
+ indeterminate={indeterminate}
127
+ checked={checked}
128
+ onOpenNestedList={() => {
129
+ setOpen(true);
130
+ setTimeout(() => {
131
+ listRef.current?.focus();
132
+ }, 0);
133
+ }}
131
134
  onSelect={handleOnSelect}
132
135
  />
133
136
  </Dropdown>
@@ -0,0 +1,12 @@
1
+ import { DropdownProps } from '@snack-uikit/dropdown';
2
+
3
+ export const FALLBACK_PLACEMENTS: DropdownProps['fallbackPlacements'] = [
4
+ 'right',
5
+ 'right-start',
6
+ 'right-end',
7
+ 'left',
8
+ 'left-start',
9
+ 'left-end',
10
+ 'bottom',
11
+ 'top',
12
+ ];
@@ -1,28 +1,38 @@
1
1
  import cn from 'classnames';
2
2
  import { ReactNode } from 'react';
3
3
 
4
+ import { Divider } from '@snack-uikit/divider';
5
+
6
+ import { useNewListContext } from '../../Lists/contexts';
4
7
  import styles from './styles.module.scss';
5
8
 
6
9
  export function PinTopGroupItem({ children }: { children: ReactNode }) {
10
+ const { size = 's' } = useNewListContext();
11
+
7
12
  if (!children) {
8
13
  return null;
9
14
  }
10
15
 
11
16
  return (
12
- <div className={cn(styles.pinTopItem)} data-test-id='list__pin-top-group-item'>
13
- {children}
17
+ <div className={cn(styles.pinTopItem)} data-size={size} data-test-id='list__pin-top-group-item'>
18
+ <div>{children}</div>
19
+
20
+ <Divider weight='regular' />
14
21
  </div>
15
22
  );
16
23
  }
17
24
 
18
25
  export function PinBottomGroupItem({ children }: { children: ReactNode }) {
26
+ const { size = 's' } = useNewListContext();
27
+
19
28
  if (!children) {
20
29
  return null;
21
30
  }
22
31
 
23
32
  return (
24
- <div className={cn(styles.pinBottomItem)} data-test-id='list__pin-bottom-group-item'>
25
- {children}
33
+ <div className={cn(styles.pinBottomItem)} data-size={size} data-test-id='list__pin-bottom-group-item'>
34
+ <Divider weight='regular' />
35
+ <div>{children}</div>
26
36
  </div>
27
37
  );
28
38
  }
@@ -1,17 +1,18 @@
1
1
  @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-dropList';
2
2
  @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
3
 
4
+ $sizes: 's', 'm', 'l';
5
+
4
6
  .pinTopItem {
5
- @include composite-var($drop-list-content-pin-top);
7
+ @each $size in $sizes {
8
+ &[data-size='#{$size}'] {
9
+ @include composite-var($drop-list, 'item', $size, 'content-pin-top');
10
+ }
11
+ }
6
12
 
7
13
  display: flex;
8
14
  flex-direction: column;
9
-
10
- border-color: $sys-neutral-decor-default;
11
- border-style: solid;
12
- border-top: 0;
13
- border-right: 0;
14
- border-left: 0;
15
+ border: none;
15
16
 
16
17
  li,
17
18
  ul {
@@ -20,16 +21,15 @@
20
21
  }
21
22
 
22
23
  .pinBottomItem {
23
- @include composite-var($drop-list-content-pin-bottom);
24
+ @each $size in $sizes {
25
+ &[data-size='#{$size}'] {
26
+ @include composite-var($drop-list, 'item', $size, 'content-pin-bottom');
27
+ }
28
+ }
24
29
 
25
30
  display: flex;
26
31
  flex-direction: column;
27
-
28
- border-color: $sys-neutral-decor-default;
29
- border-style: solid;
30
- border-right: 0;
31
- border-bottom: 0;
32
- border-left: 0;
32
+ border: none;
33
33
 
34
34
  li,
35
35
  ul {
@@ -4,17 +4,17 @@ import { FocusEvent, KeyboardEvent, RefObject } from 'react';
4
4
  import { SearchPrivate } from '@snack-uikit/search-private';
5
5
 
6
6
  import { SearchState } from '../../../types';
7
- import { useListContext, useParentListContext } from '../../Lists/contexts';
7
+ import { useNewListContext } from '../../Lists/contexts';
8
8
  import commonStyles from '../styles.module.scss';
9
9
  import styles from './styles.module.scss';
10
10
 
11
11
  export type SearchItemProps = {
12
12
  search?: SearchState;
13
+ itemRef?: RefObject<HTMLElement>;
13
14
  };
14
15
 
15
- export function SearchItem({ search }: SearchItemProps) {
16
- const { parentItemRefs, parentSetActiveFocusIndex } = useParentListContext();
17
- const { size } = useListContext();
16
+ export function SearchItem({ search, itemRef }: SearchItemProps) {
17
+ const { size = 's' } = useNewListContext();
18
18
 
19
19
  const handleKeyDown = (e: KeyboardEvent) => {
20
20
  if (['ArrowDown', 'ArrowUp'].includes(e.key)) {
@@ -23,13 +23,11 @@ export function SearchItem({ search }: SearchItemProps) {
23
23
 
24
24
  if (['ArrowRight'].includes(e.key)) {
25
25
  e.stopPropagation();
26
+ e.preventDefault();
26
27
  }
27
28
  };
28
29
 
29
30
  const handleFocus = (e: FocusEvent<HTMLInputElement>) => {
30
- if (e.target === parentItemRefs[0].current) {
31
- parentSetActiveFocusIndex?.(0);
32
- }
33
31
  e.stopPropagation();
34
32
  };
35
33
 
@@ -45,7 +43,7 @@ export function SearchItem({ search }: SearchItemProps) {
45
43
  onKeyDown={handleKeyDown}
46
44
  onFocus={handleFocus}
47
45
  {...search}
48
- ref={parentItemRefs[0] as RefObject<HTMLInputElement>}
46
+ ref={itemRef as RefObject<HTMLInputElement>}
49
47
  />
50
48
  </div>
51
49
  );
@@ -1,104 +1,124 @@
1
- import { createRef, RefObject, useEffect, useMemo } from 'react';
1
+ import { createRef, RefObject, useMemo } from 'react';
2
2
 
3
- import { extractAllChildIds, extractChildIds } from '../../utils';
4
- import { useSelectionContext } from '../Lists/contexts';
3
+ import { useNewListContext, useSelectionContext } from '../Lists/contexts';
5
4
  import { AccordionItem } from './AccordionItem';
6
5
  import { BaseItem } from './BaseItem';
7
6
  import { GroupItem } from './GroupItem';
7
+ import { GroupSelectItem } from './GroupSelectItem';
8
8
  import { NextListItem } from './NextListItem';
9
- import { ItemProps } from './types';
10
- import { addItemsIds, isAccordionItemProps, isGroupItemProps, isNextListItemProps } from './utils';
9
+ import {
10
+ Flatten,
11
+ FlattenAccordionItem,
12
+ FlattenBaseItem,
13
+ FlattenGroupSelectListItem,
14
+ FlattenNextListItem,
15
+ ItemId,
16
+ } from './types';
17
+ import { isAccordionItem, isGroupItem, isGroupSelectItem, isNextListItem } from './utils';
18
+
19
+ export function useRenderItems(focusCloseChildIds?: ItemId[]) {
20
+ const { focusFlattenItems, flattenItems } = useNewListContext();
21
+ const { isSelectionMultiple } = useSelectionContext();
22
+
23
+ return useMemo(() => {
24
+ if (!focusCloseChildIds) {
25
+ return [null];
26
+ }
11
27
 
12
- export function useRenderItems(items: ItemProps[]) {
13
- return useMemo(
14
- () =>
15
- items.map(item => {
16
- if (isGroupItemProps(item)) {
17
- return <GroupItem {...item} key={item.label} />;
18
- }
28
+ return focusCloseChildIds.map(id => {
29
+ const { itemRef, key, originalId, items } = focusFlattenItems[id];
19
30
 
20
- if (isAccordionItemProps(item)) {
21
- return <AccordionItem {...item} key={item.key} />;
22
- }
31
+ const flattenItem = flattenItems[originalId];
23
32
 
24
- if (isNextListItemProps(item)) {
25
- return <NextListItem {...item} key={item.key} />;
26
- }
33
+ if (
34
+ isGroupItem(flattenItem) ||
35
+ (!isSelectionMultiple && isGroupSelectItem<FlattenGroupSelectListItem>(flattenItem))
36
+ ) {
37
+ return <GroupItem {...flattenItem} items={items} key={key} />;
38
+ }
39
+ if (isGroupSelectItem<FlattenGroupSelectListItem>(flattenItem)) {
40
+ return <GroupSelectItem {...flattenItem} items={items} itemRef={itemRef} key={key} />;
41
+ }
42
+ if (isAccordionItem<FlattenAccordionItem>(flattenItem)) {
43
+ return <AccordionItem {...flattenItem} items={items} itemRef={itemRef} key={key} />;
44
+ }
45
+ if (isNextListItem<FlattenNextListItem>(flattenItem)) {
46
+ return <NextListItem {...flattenItem} focusId={id} items={items} itemRef={itemRef} key={key} />;
47
+ }
27
48
 
28
- return <BaseItem {...item} key={item.key} />;
29
- }),
30
- [items],
31
- );
49
+ return <BaseItem {...flattenItem} itemRef={itemRef} key={key} />;
50
+ });
51
+ }, [flattenItems, focusCloseChildIds, focusFlattenItems, isSelectionMultiple]);
32
52
  }
33
53
 
34
- type UseItemsWithIdsProps = {
54
+ type UseCreateBaseItemsProps = {
35
55
  search?: boolean;
36
56
  footerActiveElementsRefs?: RefObject<HTMLElement>[];
37
57
  };
38
58
 
39
- export function useItemsWithIds({ search, footerActiveElementsRefs }: UseItemsWithIdsProps) {
59
+ export function useCreateBaseItems({ footerActiveElementsRefs }: UseCreateBaseItemsProps): {
60
+ searchItem: Flatten<FlattenBaseItem, 'itemRef'>;
61
+ footerItems: Flatten<FlattenBaseItem, 'itemRef'>[];
62
+ } {
40
63
  return useMemo(
41
64
  () => ({
42
- search: addItemsIds(
43
- search ? ([{ itemRef: createRef<HTMLInputElement>() }] as unknown as ItemProps[]) : [],
44
- 'search',
45
- ),
46
- footerRefs: addItemsIds(
47
- footerActiveElementsRefs
48
- ? (footerActiveElementsRefs?.map(ref => ({ itemRef: ref })) as unknown as ItemProps[])
49
- : [],
50
- 'footer',
51
- ),
65
+ searchItem: {
66
+ itemRef: createRef<HTMLInputElement>(),
67
+ id: '~search',
68
+ parentId: '~main',
69
+ items: [],
70
+ allChildIds: [],
71
+ },
72
+ footerItems:
73
+ footerActiveElementsRefs?.map((itemRef, idx) => ({
74
+ id: `~footer__${idx}`,
75
+ itemRef,
76
+ parentId: '~main',
77
+ items: [],
78
+ allChildIds: [],
79
+ })) ?? [],
52
80
  }),
53
-
54
- [footerActiveElementsRefs, search],
81
+ [footerActiveElementsRefs],
55
82
  );
56
83
  }
57
84
 
58
85
  type UseGroupItemSelectionProps = {
59
- items: ItemProps[];
60
- id?: string | number;
86
+ allChildIds: ItemId[];
87
+ items: ItemId[];
88
+ id: ItemId;
61
89
  disabled?: boolean;
62
90
  };
63
91
 
64
- export function useGroupItemSelection({ id, items, disabled }: UseGroupItemSelectionProps) {
92
+ export function useGroupItemSelection({ id, allChildIds }: UseGroupItemSelectionProps) {
65
93
  const { value, setValue, isSelectionMultiple } = useSelectionContext();
66
- const { childIds, allChildIds } = useMemo(
67
- () => ({ childIds: extractChildIds({ items }), allChildIds: extractAllChildIds({ items }) }),
68
- [items],
94
+ const { flattenItems } = useNewListContext();
95
+
96
+ const baseChildIds = useMemo(
97
+ () =>
98
+ allChildIds.filter(itemId => {
99
+ const item = flattenItems[itemId];
100
+
101
+ return item && !('type' in item);
102
+ }),
103
+ [allChildIds, flattenItems],
69
104
  );
70
105
 
71
- const isIndeterminate = isSelectionMultiple
72
- ? allChildIds.some(childId => value?.includes(childId))
73
- : allChildIds.includes(value ?? '');
74
- const checked = isSelectionMultiple ? allChildIds.every(childId => value?.includes(childId)) : undefined;
106
+ const checked = isSelectionMultiple
107
+ ? value && Boolean(value.length) && baseChildIds.every(childId => value?.includes(childId))
108
+ : undefined;
75
109
 
76
- useEffect(() => {
77
- if (isSelectionMultiple) {
78
- if (checked && !value?.includes(id)) {
79
- setValue?.((value: Array<number | string>) => (value ?? []).concat([id ?? '']));
80
- }
81
- if (!checked && value?.includes(id)) {
82
- setValue?.((value: Array<number | string>) => (value ?? []).filter(itemId => itemId !== id));
83
- }
84
- }
85
- }, [checked, disabled, id, isSelectionMultiple, setValue, value]);
110
+ const indeterminate = isSelectionMultiple
111
+ ? !checked && baseChildIds.some(childId => value?.includes(childId))
112
+ : baseChildIds.includes(value ?? '');
86
113
 
87
114
  const handleOnSelect = () => {
88
115
  if (checked) {
89
- setValue?.((value: Array<string | number>) =>
90
- (value ?? []).filter(itemId => itemId !== id && !childIds.includes(itemId)),
91
- );
92
- return;
93
- }
94
-
95
- if (isIndeterminate) {
96
- setValue?.((value: Array<string | number>) => Array.from(new Set([...(value ?? []), ...childIds, id])));
116
+ setValue?.((value: ItemId[]) => (value ?? []).filter(itemId => itemId !== id && !baseChildIds.includes(itemId)));
97
117
  return;
98
118
  }
99
119
 
100
- setValue?.((value: Array<string | number>) => (value ?? []).concat([...childIds, id ?? '']));
120
+ setValue?.((value: ItemId[]) => Array.from(new Set([...(value ?? []), ...baseChildIds])));
101
121
  };
102
122
 
103
- return { checked, isIndeterminate, handleOnSelect };
123
+ return { checked, indeterminate, handleOnSelect };
104
124
  }
@@ -1,9 +1,5 @@
1
- export * from './BaseItem';
2
- export * from './NextListItem';
3
- export * from './AccordionItem';
4
- export * from './GroupItem';
5
- export * from './SearchItem';
6
1
  export * from './hooks';
7
2
  export * from './types';
8
3
  export * from './utils';
4
+ export * from './SearchItem';
9
5
  export * from './PinGroupItem';
@@ -3,35 +3,13 @@
3
3
 
4
4
  $sizes: 's', 'm', 'l';
5
5
 
6
- .listItem {
7
- cursor: pointer;
8
-
6
+ .itemWrapper {
9
7
  position: relative;
10
-
11
- display: flex;
12
- flex-direction: row;
13
- align-items: center;
14
-
15
- box-sizing: border-box;
16
- width: max-content;
17
8
  min-width: 100%;
18
9
  margin: 0;
19
10
  padding: 0;
20
11
 
21
- color: $sys-neutral-text-main;
22
- text-align: start;
23
-
24
- appearance: unset;
25
- background-color: transparent;
26
- border: none;
27
- outline: none;
28
-
29
- @each $size in $sizes {
30
- &[data-size='#{$size}'] {
31
- @include composite-var($drop-list, 'item', $size, 'container');
32
- }
33
- }
34
-
12
+ &::before,
35
13
  &::after {
36
14
  pointer-events: none;
37
15
  content: '';
@@ -45,6 +23,7 @@ $sizes: 's', 'm', 'l';
45
23
 
46
24
  background-color: transparent;
47
25
  }
26
+
48
27
  &[data-disabled],
49
28
  &:disabled {
50
29
  cursor: not-allowed;
@@ -66,12 +45,44 @@ $sizes: 's', 'm', 'l';
66
45
  &[data-focused],
67
46
  &:hover,
68
47
  &:focus-visible {
69
- &::after {
48
+ &::before {
70
49
  opacity: $opacity-a008;
71
50
  background-color: $sys-neutral-accent-default;
72
51
  }
73
52
  }
53
+ }
54
+ }
74
55
 
56
+ .listItem {
57
+ cursor: pointer;
58
+
59
+ position: relative;
60
+
61
+ display: flex;
62
+ flex-direction: row;
63
+ align-items: center;
64
+
65
+ box-sizing: border-box;
66
+ width: max-content;
67
+ min-width: 100%;
68
+ margin: 0;
69
+ padding: 0;
70
+
71
+ color: $sys-neutral-text-main;
72
+ text-align: start;
73
+
74
+ appearance: unset;
75
+ background-color: transparent;
76
+ border: none;
77
+ outline: none;
78
+
79
+ @each $size in $sizes {
80
+ &[data-size='#{$size}'] {
81
+ @include composite-var($drop-list, 'item', $size, 'container');
82
+ }
83
+ }
84
+
85
+ &:not([data-inactive], [data-disabled]) {
75
86
  &[data-focused],
76
87
  &:focus-visible {
77
88
  @include outline-inside-var($container-focused-s);