polpo 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.storybook/theme.ts +2 -2
  2. package/.turbo/turbo-build.log +0 -77
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/README.md +2 -5
  5. package/dist/chunk-CFYQBHH5.js +3 -0
  6. package/dist/chunk-CFYQBHH5.js.map +1 -0
  7. package/dist/chunk-MAWW6AA7.js +3 -0
  8. package/dist/chunk-MAWW6AA7.js.map +1 -0
  9. package/dist/get-modal-position-drle0OjP.d.cts +49 -0
  10. package/dist/get-modal-position-drle0OjP.d.ts +49 -0
  11. package/dist/helpers.cjs +1 -1
  12. package/dist/helpers.cjs.map +1 -1
  13. package/dist/helpers.d.cts +9 -2
  14. package/dist/helpers.d.ts +9 -2
  15. package/dist/helpers.js +1 -1
  16. package/dist/hooks.cjs +1 -1
  17. package/dist/hooks.cjs.map +1 -1
  18. package/dist/hooks.d.cts +59 -21
  19. package/dist/hooks.d.ts +59 -21
  20. package/dist/hooks.js +1 -1
  21. package/dist/ui.cjs +601 -389
  22. package/dist/ui.cjs.map +1 -1
  23. package/dist/ui.d.cts +97 -77
  24. package/dist/ui.d.ts +97 -77
  25. package/dist/ui.js +585 -373
  26. package/dist/ui.js.map +1 -1
  27. package/dist/use-modal-in-container-DiNW1PE_.d.cts +34 -0
  28. package/dist/use-modal-in-container-neGo-kMk.d.ts +34 -0
  29. package/package.json +5 -5
  30. package/src/components/buttons/button/button.stories.tsx +4 -4
  31. package/src/components/buttons/button/button.style.ts +10 -5
  32. package/src/components/buttons/button/button.tsx +7 -19
  33. package/src/components/cards/flip-card/flip-card.tsx +1 -1
  34. package/src/components/cursor/cursor.stories.tsx +35 -0
  35. package/src/components/cursor/cursor.style.ts +73 -0
  36. package/src/components/cursor/cursor.tsx +49 -0
  37. package/src/components/cursor/index.ts +1 -0
  38. package/src/components/form/checkbox/checkbox.stories.tsx +51 -0
  39. package/src/components/form/checkbox/checkbox.style.ts +73 -37
  40. package/src/components/form/checkbox/checkbox.tsx +38 -4
  41. package/src/components/form/field/field.stories.tsx +5 -1
  42. package/src/components/form/field/field.style.ts +12 -0
  43. package/src/components/form/field/field.tsx +3 -1
  44. package/src/components/form/field/field.types.ts +6 -0
  45. package/src/components/form/input-color/input-color.style.ts +5 -4
  46. package/src/components/form/input-color/input-color.tsx +41 -44
  47. package/src/components/form/radio/radio.stories.tsx +29 -5
  48. package/src/components/form/radio/radio.style.ts +45 -24
  49. package/src/components/form/radio/radio.tsx +22 -3
  50. package/src/components/form/select/options.tsx +119 -67
  51. package/src/components/form/select/select.stories.tsx +103 -42
  52. package/src/components/form/select/select.style.ts +10 -92
  53. package/src/components/form/select/select.tsx +19 -42
  54. package/src/components/form/select/select.types.ts +4 -21
  55. package/src/components/form/slider/slider.style.ts +2 -0
  56. package/src/components/icon/icons/social.tsx +17 -1
  57. package/src/components/index.ts +1 -0
  58. package/src/components/infinity-scroll/infinity-scroll.tsx +1 -1
  59. package/src/components/line/line.stories.tsx +3 -4
  60. package/src/components/modals/action-modal/action-modal.stories.tsx +58 -39
  61. package/src/components/modals/action-modal/action-modal.style.ts +13 -25
  62. package/src/components/modals/action-modal/action-modal.tsx +68 -70
  63. package/src/components/modals/aside-modal/aside-modal.stories.tsx +11 -15
  64. package/src/components/modals/aside-modal/aside-modal.style.ts +17 -37
  65. package/src/components/modals/aside-modal/aside-modal.tsx +41 -43
  66. package/src/components/modals/confirmation-modal/confirmation-modal.stories.tsx +21 -9
  67. package/src/components/modals/index.ts +2 -0
  68. package/src/components/modals/menu/index.ts +1 -0
  69. package/src/components/modals/menu/menu.stories.tsx +69 -0
  70. package/src/components/modals/menu/menu.style.ts +62 -0
  71. package/src/components/modals/menu/menu.tsx +142 -0
  72. package/src/components/modals/modal/backdrop.tsx +70 -0
  73. package/src/components/modals/modal/index.ts +1 -0
  74. package/src/components/modals/modal/modal.stories.tsx +325 -0
  75. package/src/components/modals/modal/modal.style.ts +62 -2
  76. package/src/components/modals/modal/modal.tsx +82 -123
  77. package/src/components/modals/portal/index.ts +1 -0
  78. package/src/components/modals/portal/portal.tsx +18 -0
  79. package/src/components/tabs/tabs-list.tsx +13 -10
  80. package/src/components/tabs/tabs.style.ts +48 -43
  81. package/src/components/tag/tag.stories.tsx +11 -12
  82. package/src/components/tag/tag.style.ts +9 -4
  83. package/src/components/tag/tag.tsx +2 -12
  84. package/src/components/tooltips/tooltip/tooltip.stories.tsx +5 -2
  85. package/src/components/tooltips/tooltip/tooltip.style.ts +37 -6
  86. package/src/components/tooltips/tooltip/tooltip.tsx +33 -19
  87. package/src/components/typography/typography.stories.tsx +3 -1
  88. package/src/components/typography/typography.tsx +21 -0
  89. package/src/contexts/theme-context/theme.animations.ts +91 -2
  90. package/src/contexts/theme-context/theme.defaults.ts +1 -1
  91. package/src/core/http-client.ts +49 -47
  92. package/src/core/variants/color.ts +3 -30
  93. package/src/core/variants/radius.ts +12 -41
  94. package/src/core/variants/size.ts +8 -33
  95. package/src/helpers/get-modal-position-relative-to-screen.ts +86 -0
  96. package/src/helpers/get-modal-position.ts +173 -28
  97. package/src/helpers/index.ts +1 -0
  98. package/src/hooks/index.ts +9 -3
  99. package/src/hooks/use-click-outside.ts +32 -0
  100. package/src/hooks/use-cookie.ts +124 -0
  101. package/src/hooks/use-dimensions.ts +11 -14
  102. package/src/hooks/use-dom-container.ts +32 -0
  103. package/src/hooks/use-event-listener.ts +4 -4
  104. package/src/hooks/use-geolocation.ts +63 -0
  105. package/src/hooks/use-in-view.ts +9 -11
  106. package/src/hooks/use-intersection-observer.ts +19 -0
  107. package/src/hooks/use-modal-in-container.ts +60 -52
  108. package/src/hooks/use-modal-transition.ts +54 -0
  109. package/src/hooks/use-modal.ts +21 -0
  110. package/src/hooks/use-mouse-position.ts +55 -7
  111. package/src/hooks/use-resize-observer.ts +18 -0
  112. package/src/stories/GettingStarted.mdx +2 -6
  113. package/svg/Name=npm, Category=social.svg +3 -0
  114. package/tsconfig.json +1 -0
  115. package/vite.config.ts +1 -0
  116. package/.turbo/daemon/f5c5c8fb195b01d0-turbo.log.2024-05-26 +0 -0
  117. package/.turbo/turbo-build$colon$watch.log +0 -96
  118. package/.turbo/turbo-build-storybook.log +0 -0
  119. package/.turbo/turbo-lint$colon$fix.log +0 -2
  120. package/dist/chunk-M4KRSYE7.js +0 -3
  121. package/dist/chunk-M4KRSYE7.js.map +0 -1
  122. package/dist/chunk-U5XSMSKZ.js +0 -3
  123. package/dist/chunk-U5XSMSKZ.js.map +0 -1
  124. package/dist/get-modal-position-DPftPoU2.d.cts +0 -28
  125. package/dist/get-modal-position-DPftPoU2.d.ts +0 -28
  126. package/src/components/form/select/select-option.tsx +0 -84
  127. package/src/hooks/use-observer.ts +0 -18
  128. package/src/hooks/use-on-click-outside-ref.ts +0 -17
@@ -1,15 +1,12 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { useTheme } from 'styled-components';
3
3
 
4
- import { InfinityScroll } from '../../infinity-scroll';
5
- import { Modal } from '../../modals';
6
- import { Typography } from '../../typography';
4
+ import { Menu } from '../../modals';
7
5
 
8
- import { SelectOption } from './select-option';
9
- import { OptionsHeaderStyle, OptionsStyle } from './select.style';
6
+ import { OptionsHeaderStyle, OptionsMenuStyle } from './select.style';
10
7
  import { OptionsProps, SelectItem } from './select.types';
11
8
 
12
- import { useEventListener, useMediaQuery } from '@polpo/hooks';
9
+ import { useClassNames, useEventListener, useMediaQuery, useResizeObserver } from '@polpo/hooks';
13
10
 
14
11
  export const Options = <T extends SelectItem>({
15
12
  onSearchQuery,
@@ -21,17 +18,19 @@ export const Options = <T extends SelectItem>({
21
18
  selectOption,
22
19
  unselectOption,
23
20
  isOpen,
24
- style,
25
21
  options,
26
22
  loadMore = () => null,
27
23
  isLoading = false,
28
24
  hasNextPage = false,
29
- modalRef,
25
+ containerRef,
30
26
  Component,
31
- variant,
27
+ onClose,
28
+ emptyMessage = 'No options to select',
29
+ maxHeight = 400,
32
30
  }: OptionsProps<T>) => {
33
31
  const theme = useTheme();
34
- const isMobile = useMediaQuery(`(min-width: ${theme.constants.breakpoints.mobileL})`);
32
+ const modalContainerRef = useRef<HTMLElement>(null);
33
+ const isMobile = useMediaQuery(`(max-width: ${theme.constants.breakpoints.mobileL})`);
35
34
  const [internalSearchQuery, setInternalSearchQuery] = useState('');
36
35
  const searchInputRef = useRef<HTMLInputElement>(null);
37
36
 
@@ -70,71 +69,124 @@ export const Options = <T extends SelectItem>({
70
69
  if (searchInputRef.current) {
71
70
  searchInputRef.current.focus();
72
71
  } else {
73
- modalRef.current?.focus();
72
+ modalContainerRef.current?.focus();
74
73
  }
75
74
  }
76
- }, [isOpen, modalRef]);
75
+ }, [isOpen, modalContainerRef]);
76
+
77
+ const optionIsSelected = useCallback(
78
+ (option: T) => !!value && value !== '' && compareValueOrValuesAreEqual(option, value),
79
+ [compareValueOrValuesAreEqual, value],
80
+ );
81
+
82
+ const handleKeyDown = useCallback(
83
+ (option: T) => (e: React.KeyboardEvent) => {
84
+ if (['Enter', ' '].includes(e.key)) {
85
+ e.preventDefault();
86
+
87
+ const selected = optionIsSelected(option);
88
+
89
+ if (selected && multiselect) {
90
+ unselectOption(option);
91
+ } else {
92
+ selectOption(option);
93
+ }
94
+ }
95
+ },
96
+ [multiselect, selectOption, optionIsSelected, unselectOption],
97
+ );
77
98
 
78
99
  const renderInternalOption = useCallback(
79
- (option: T, key: number) => (
80
- <SelectOption
81
- key={key}
82
- id={`${key}`}
83
- selected={!!value && value !== '' && compareValueOrValuesAreEqual(option, value)}
84
- data={option}
85
- multiselect={multiselect}
86
- unselectOption={unselectOption}
87
- selectOption={selectOption}
88
- Component={Component}
89
- variant={variant}
90
- />
91
- ),
92
- [value, compareValueOrValuesAreEqual, multiselect, unselectOption, selectOption, Component, variant],
100
+ (option: T, key: number) => {
101
+ const selected = optionIsSelected(option);
102
+
103
+ return (
104
+ <Menu.Option
105
+ key={key}
106
+ id={`${key}`}
107
+ label={<Component data={option} isSelected={selected} multiselect={multiselect} />}
108
+ onClick={(selected: boolean) => {
109
+ if (multiselect) {
110
+ if (selected) selectOption(option);
111
+ else unselectOption(option);
112
+ } else {
113
+ selectOption(option);
114
+ }
115
+ }}
116
+ onKeyDown={handleKeyDown(option)}
117
+ asCheckbox={multiselect}
118
+ selected={selected}
119
+ />
120
+ );
121
+ },
122
+ [optionIsSelected, Component, multiselect, handleKeyDown, selectOption, unselectOption],
93
123
  );
94
124
 
125
+ const [height, setHeight] = useState<string>('400px');
126
+
127
+ const getHeight = useCallback(() => {
128
+ const containerBottom = containerRef.current?.getBoundingClientRect().bottom ?? 0;
129
+
130
+ const height = Math.min(window.innerHeight - containerBottom - 20, maxHeight);
131
+
132
+ setHeight(`${Math.round(height)}px`);
133
+ }, [containerRef, maxHeight]);
134
+
135
+ useResizeObserver(containerRef, getHeight);
136
+ useEventListener('resize', getHeight);
137
+
138
+ const menuClassName = useClassNames({
139
+ 'search-input': Boolean(onSearchQuery),
140
+ });
141
+
95
142
  return (
96
- <Modal
97
- isOpen={isOpen}
143
+ <OptionsMenuStyle
98
144
  id='form-select'
99
- backdrop={isMobile ? 'transparent' : 'blur'}
100
- opacity={isMobile ? 0 : 0.8}
101
- zIndex={150}
145
+ isOpen={isOpen}
146
+ onClose={onClose}
147
+ backdrop={isMobile ? 'blur' : 'opaque'}
148
+ opacity={isMobile ? 0.8 : 0.4}
149
+ position={isMobile ? 'center' : 'bottom'}
150
+ offset={5}
151
+ transitionDuration={200}
152
+ containerRef={isMobile ? undefined : containerRef}
153
+ className={menuClassName}
154
+ style={
155
+ isMobile
156
+ ? {
157
+ maxHeight: window.innerHeight - 100,
158
+ width: window.innerWidth - 100,
159
+ }
160
+ : {
161
+ maxHeight: height,
162
+ minHeight: '200px',
163
+ width: containerRef.current?.offsetWidth ?? 'auto',
164
+ }
165
+ }
166
+ rootStyle={isMobile ? {} : {}}
102
167
  >
103
- <OptionsStyle ref={modalRef} style={{ ...style, zIndex: 150 }} tabIndex={-1}>
104
- {onSearchQuery && (
105
- <OptionsHeaderStyle>
106
- <input
107
- name='query'
108
- className='input-search'
109
- value={searchQueryValue ?? internalSearchQuery}
110
- onChange={handleSearchQuery}
111
- placeholder={searchQueryPlaceholder}
112
- onClick={e => e.stopPropagation()}
113
- ref={searchInputRef}
114
- autoFocus
115
- />
116
- </OptionsHeaderStyle>
117
- )}
118
- <section className='options-list-container' tabIndex={-1}>
119
- <ul className='options-list' role='listbox'>
120
- {options.length === 0 ? (
121
- <li className='option option-empty' tabIndex={-1}>
122
- <Typography noPadding variant='label'>
123
- No options to select
124
- </Typography>
125
- </li>
126
- ) : (
127
- <InfinityScroll
128
- isLoading={isLoading}
129
- hasNextPage={hasNextPage}
130
- loadMore={loadMore}
131
- data={options}
132
- renderItem={renderInternalOption}
133
- />
134
- )}
135
- </ul>
136
- </section>
137
- </OptionsStyle>
138
- </Modal>
168
+ {onSearchQuery && (
169
+ <OptionsHeaderStyle>
170
+ <input
171
+ name='query'
172
+ className='input-search'
173
+ value={searchQueryValue ?? internalSearchQuery}
174
+ onChange={handleSearchQuery}
175
+ placeholder={searchQueryPlaceholder}
176
+ onClick={e => e.stopPropagation()}
177
+ ref={searchInputRef}
178
+ autoFocus
179
+ />
180
+ </OptionsHeaderStyle>
181
+ )}
182
+ <Menu.OptionsGroup
183
+ isLoading={isLoading}
184
+ hasNextPage={hasNextPage}
185
+ loadMore={loadMore}
186
+ data={options}
187
+ renderItem={renderInternalOption}
188
+ emptyMessage={emptyMessage}
189
+ />
190
+ </OptionsMenuStyle>
139
191
  );
140
192
  };
@@ -7,7 +7,7 @@ import { Select } from './select';
7
7
 
8
8
  import type { Meta, StoryObj } from '@storybook/react';
9
9
 
10
- const meta: Meta<typeof Select> = {
10
+ const meta: Meta<typeof Select<string>> = {
11
11
  title: 'Form/Select',
12
12
  component: Select,
13
13
  argTypes: {
@@ -25,65 +25,126 @@ const meta: Meta<typeof Select> = {
25
25
  maxOptions: { control: { type: 'number' } },
26
26
  showClearOption: { control: 'boolean' },
27
27
  multiselect: { control: false },
28
- optionVariant: {
29
- control: 'select',
30
- options: [undefined, 'checkbox', 'icon', 'default'],
31
- if: { arg: 'multiselect' },
32
- },
33
28
  },
34
29
  args: {
35
30
  label: 'Select',
36
31
  searchQueryPlaceholder: 'Search',
37
32
  ...FieldSharedArgs,
38
- optionVariant: undefined,
33
+ placeholder: 'Select an option',
34
+ options: [
35
+ 'Leda Pinna',
36
+ 'Antonio Sansone',
37
+ 'Orlando Simeone',
38
+ 'Giorgio Villa',
39
+ 'Virgilio Paoli',
40
+ 'Nicola Loi',
41
+ 'Giordano Manzo',
42
+ 'Emilio Galletti',
43
+ 'Moira Galimberti',
44
+ 'Fedele Spina',
45
+ 'Débora Buzzi',
46
+ 'Ferrari Sacco',
47
+ 'Rosalba Lodi',
48
+ 'Bianca Paris',
49
+ 'Salvatrice di Paola',
50
+ 'Antonietta Mancuso',
51
+ 'Corradina Battistini',
52
+ 'Elisabeth Annunziata',
53
+ 'Federica Vinciguerra',
54
+ 'Ennio Spinelli',
55
+ 'Susanna Franzoni',
56
+ 'Ottavio di Bella',
57
+ 'Melania Genovese',
58
+ 'Marzio Tomasi',
59
+ 'Anselmo Bettoni',
60
+ 'Robert Boni',
61
+ 'Leonardo Guido',
62
+ 'Ermelinda Biagini',
63
+ 'Aurélio Falcone',
64
+ 'Hubert Paolini',
65
+ 'Imelda Sessa',
66
+ 'Santino Viviani',
67
+ 'Peter Franchini',
68
+ 'Guglielmo Lorenzon',
69
+ 'Martha Casiraghi',
70
+ 'Stefano Cuomo',
71
+ 'Valéria di Giovanni',
72
+ 'Cecília Ambrosio',
73
+ 'Vincenzina Bernardini',
74
+ 'Renato Marchi',
75
+ 'Raffaello Guida',
76
+ 'Gregório Battisti',
77
+ 'Alberto Bruni',
78
+ 'Giovannino Lepore',
79
+ 'Ludovica Randazzo',
80
+ 'Iris Merli',
81
+ 'Alida Fois',
82
+ 'Innocenzo Palazzo',
83
+ 'Giacinto Vergani',
84
+ 'Alessio Drago',
85
+ ],
86
+ renderOption: v => v,
39
87
  },
40
88
  decorators: [ContainerDecorator],
89
+ };
90
+
91
+ export default meta;
92
+ type Story = StoryObj<typeof Select<string>>;
93
+
94
+ export const SingleValue: Story = {
95
+ args: {},
41
96
  render: args => {
42
97
  const [value, setValue] = useState<string | null>(null);
43
98
 
99
+ return <Select<string> {...args} multiselect={false} value={value} setValue={value => setValue(value)} />;
100
+ },
101
+ };
102
+
103
+ export const MultipleValues: Story = {
104
+ render: args => {
105
+ const [value, setValue] = useState<Array<string>>([]);
106
+
107
+ return <Select<string> {...args} multiselect={true} value={value} setValue={value => setValue(value)} />;
108
+ },
109
+ };
110
+
111
+ export const SearchQuery: Story = {
112
+ render: ({ options, ...args }) => {
113
+ const [value, setValue] = useState<Array<string>>([]);
114
+ const [searchQueryValue, setSearchQueryValue] = useState<string>('');
115
+
44
116
  return (
45
- <Select
117
+ <Select<string>
46
118
  {...args}
47
- options={[
48
- 'A',
49
- 'B',
50
- 'C',
51
- 'D',
52
- 'E',
53
- 'F',
54
- 'H',
55
- 'I',
56
- 'J',
57
- 'K',
58
- 'L',
59
- 'M',
60
- 'N',
61
- 'O',
62
- 'P',
63
- 'Q',
64
- 'R',
65
- 'S',
66
- 'T',
67
- 'U',
68
- 'V',
69
- 'W',
70
- 'X',
71
- 'Y',
72
- 'Z',
73
- ]}
119
+ options={options.filter(option => option.toLowerCase().includes(searchQueryValue.toLowerCase()))}
120
+ multiselect={true}
74
121
  value={value}
75
- renderOption={v => v}
76
- optionVariant={undefined}
77
- multiselect={false}
78
122
  setValue={value => setValue(value)}
123
+ searchQueryPlaceholder='Search option'
124
+ searchQueryValue={searchQueryValue}
125
+ onSearchQuery={setSearchQueryValue}
79
126
  />
80
127
  );
81
128
  },
82
129
  };
83
130
 
84
- export default meta;
85
- type Story = StoryObj<typeof Select>;
131
+ export const SearchQueryWithClearOption: Story = {
132
+ render: ({ options, ...args }) => {
133
+ const [value, setValue] = useState<Array<string>>([]);
134
+ const [searchQueryValue, setSearchQueryValue] = useState<string>('');
86
135
 
87
- export const Default: Story = {
88
- args: {},
136
+ return (
137
+ <Select<string>
138
+ {...args}
139
+ options={options.filter(option => option.toLowerCase().includes(searchQueryValue.toLowerCase()))}
140
+ multiselect={true}
141
+ value={value}
142
+ showClearOption={true}
143
+ setValue={value => setValue(value)}
144
+ searchQueryPlaceholder='Search option'
145
+ searchQueryValue={searchQueryValue}
146
+ onSearchQuery={setSearchQueryValue}
147
+ />
148
+ );
149
+ },
89
150
  };
@@ -1,5 +1,7 @@
1
1
  import styled from 'styled-components';
2
2
 
3
+ import { Menu } from '@polpo/ui';
4
+
3
5
  export const SelectStyle = styled.section`
4
6
  cursor: pointer;
5
7
 
@@ -57,106 +59,22 @@ export const SelectStyle = styled.section`
57
59
  }
58
60
  `;
59
61
 
60
- export const OptionsStyle = styled.section`
61
- position: fixed;
62
- border-radius: 4px;
63
- border: 1px solid ${props => props.theme.colors.primary.main};
64
- box-shadow: 0 3px 7px 0 rgba(145, 145, 145, 0.13);
65
- overflow: auto;
66
- background: ${props => props.theme.colors.background.main};
67
- color: ${props => props.theme.colors.text.main};
68
- display: grid;
69
- gap: 5px;
70
- align-content: start;
71
- outline: 0;
72
- top: 50%;
73
- left: 50%;
74
- transform: translate(-50%, -50%);
75
- width: 80vw;
76
- max-height: 80dvh;
77
- min-width: fit-content;
78
-
79
- .options-list-container {
80
- height: 100%;
81
- overflow: auto;
82
- outline: 0;
83
- }
62
+ export const OptionsMenuStyle = styled(Menu)`
63
+ overflow-y: auto;
84
64
 
85
- .options-list {
86
- list-style: none;
65
+ &.search-input {
87
66
  display: grid;
88
- margin: 0;
89
- padding: 0;
90
- outline: 0;
91
- }
92
-
93
- .options-selected {
94
- border-bottom: 1px solid #9e9e9e;
95
- padding-bottom: 5px;
96
- }
97
-
98
- .option-empty {
99
- color: #919191;
100
- }
101
- `;
102
-
103
- export const OptionStyle = styled.li`
104
- padding: 0.5em 1em;
105
- font-size: 0.8em;
106
- white-space: nowrap;
107
- text-overflow: ellipsis;
108
- overflow: hidden;
109
- display: grid;
110
- grid-template-columns: 1fr;
111
- gap: 1em;
112
- align-items: center;
113
- cursor: pointer;
114
- outline: 0;
115
-
116
- &.multiselect-icon {
117
- grid-template-columns: 1fr auto;
118
- }
119
-
120
- &.multiselect-checkbox {
121
- grid-template-columns: auto 1fr;
122
- }
123
-
124
- .option-content {
125
- white-space: nowrap;
126
- text-overflow: ellipsis;
127
- overflow: hidden;
128
- }
129
-
130
- &:hover {
131
- background: ${props => props.theme.colors.text.main}11;
132
- }
133
-
134
- &:focus {
135
- background: ${props => props.theme.colors.text.main}22;
136
- }
137
-
138
- &.selected-option,
139
- &[aria-selected='true'] {
140
- background: ${props => props.theme.colors.primary.main}33;
141
-
142
- &:hover {
143
- background: ${props => props.theme.colors.primary.main}22;
144
- }
145
-
146
- &:focus {
147
- background: ${props => props.theme.colors.primary.main}11;
148
- }
67
+ grid-template-rows: auto 1fr;
68
+ grid-template-columns: 1fr;
149
69
  }
150
70
  `;
151
71
 
152
72
  export const OptionsHeaderStyle = styled.section`
153
- display: grid;
154
- gap: 5px;
155
- padding-bottom: 5px;
73
+ margin: 5px;
156
74
 
157
75
  .input-search {
158
- padding: 8px 6px;
159
- border: 1px solid #b4b4b4;
76
+ padding: 0.8em 1em;
77
+ border: 1px solid ${props => props.theme.colors.border.main};
160
78
  font-size: 0.8em;
161
79
  width: 100%;
162
80
  border-radius: 4px;
@@ -1,5 +1,4 @@
1
- import React, { useCallback, useMemo } from 'react';
2
- import { useTheme } from 'styled-components';
1
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
3
2
 
4
3
  import { Icon } from '../../icon';
5
4
  import { Typography } from '../../typography';
@@ -15,23 +14,12 @@ import {
15
14
  MultiValue,
16
15
  OptionComponentProps,
17
16
  SelectItem,
18
- SelectOptionVariant,
19
17
  SelectValue,
20
18
  SingleSelectProps,
21
19
  SingleValue,
22
20
  UnControlledSelectProps,
23
21
  } from './select.types';
24
22
 
25
- import { useMediaQuery, useModalInContainer } from '@polpo/hooks';
26
-
27
- /*
28
- *type SelectContextState<T extends SelectItem = unknown> = {
29
- *value: T;
30
- *};
31
- *
32
- *const SelectContext = createContext<SelectContextState | null>(null);
33
- */
34
-
35
23
  export const Select = <T extends SelectItem>({
36
24
  // Select props
37
25
  options,
@@ -45,7 +33,6 @@ export const Select = <T extends SelectItem>({
45
33
  hasNextPage,
46
34
  multiselect,
47
35
  maxOptions,
48
- optionVariant = SelectOptionVariant.DEFAULT,
49
36
  // Shared props
50
37
  name,
51
38
  value,
@@ -74,19 +61,18 @@ export const Select = <T extends SelectItem>({
74
61
  ...fieldProps
75
62
  }: UnControlledSelectProps<T>) => {
76
63
  const id = useMemo(() => crypto.randomUUID(), []);
77
- const theme = useTheme();
78
- const isMobile = useMediaQuery(`(min-width: ${theme.constants.breakpoints.mobileL})`);
79
- const { modalRef, isVisible, setIsVisible, modalStyle, containerRef } = useModalInContainer({
80
- position: 'bottom',
81
- distancePercentage: 50,
82
- offset: 5,
83
- });
64
+ const containerRef = useRef<HTMLElement>(null);
65
+ const [isOpen, setIsOpen] = useState(false);
84
66
 
85
67
  const openSelect = useCallback(
86
68
  (open: boolean) => {
87
- setIsVisible(open && !disabled);
69
+ if (open && !disabled) {
70
+ setIsOpen(true);
71
+ } else {
72
+ setIsOpen(false);
73
+ }
88
74
  },
89
- [disabled, setIsVisible],
75
+ [disabled],
90
76
  );
91
77
 
92
78
  const compareValuesIsEqual = useCallback(
@@ -167,10 +153,10 @@ export const Select = <T extends SelectItem>({
167
153
  setValue(filteredValues.length === 0 ? [] : filteredValues);
168
154
  } else {
169
155
  setValue(null);
170
- setIsVisible(false);
156
+ setIsOpen(false);
171
157
  }
172
158
  },
173
- [compareValuesIsEqual, multiselect, setIsVisible, setValue, value],
159
+ [compareValuesIsEqual, multiselect, setValue, value],
174
160
  );
175
161
 
176
162
  const selectOption = useCallback(
@@ -183,10 +169,10 @@ export const Select = <T extends SelectItem>({
183
169
  setValue([...(value as Array<T>), selectedOption] as MultiValue<T>);
184
170
  } else {
185
171
  setValue(selectedOption as SingleValue<T>);
186
- setIsVisible(false);
172
+ setIsOpen(false);
187
173
  }
188
174
  },
189
- [maxOptions, multiselect, setIsVisible, setValue, value],
175
+ [maxOptions, multiselect, setValue, value],
190
176
  );
191
177
 
192
178
  const clearOption = useCallback(
@@ -214,7 +200,7 @@ export const Select = <T extends SelectItem>({
214
200
  <Field
215
201
  id={id}
216
202
  error={error}
217
- isFocus={isVisible}
203
+ isFocus={isOpen}
218
204
  onClickLeftIcon={() => openSelect(true)}
219
205
  onClickRightIcon={() => openSelect(true)}
220
206
  ref={containerRef}
@@ -229,7 +215,7 @@ export const Select = <T extends SelectItem>({
229
215
  type='button'
230
216
  className={`input-button ${(Array.isArray(value) ? value.length > 0 : value) ? '' : 'placeholder'}`}
231
217
  aria-haspopup='listbox'
232
- aria-expanded={isVisible}
218
+ aria-expanded={isOpen}
233
219
  onFocus={e => {
234
220
  openSelect(true);
235
221
  onFocus && onFocus(e);
@@ -246,11 +232,12 @@ export const Select = <T extends SelectItem>({
246
232
  <Icon name='cross' />
247
233
  </section>
248
234
  )}
249
- <Icon name={isVisible ? 'caret-up' : 'caret-down'} />
235
+ <Icon name={isOpen ? 'caret-up' : 'caret-down'} />
250
236
  </section>
251
237
  <Options
252
- modalRef={modalRef}
253
- isOpen={isVisible}
238
+ containerRef={containerRef}
239
+ isOpen={isOpen}
240
+ onClose={() => setIsOpen(false)}
254
241
  value={value}
255
242
  compareValueOrValuesAreEqual={compareValueOrValuesAreEqual}
256
243
  Component={OptionComponent}
@@ -260,20 +247,10 @@ export const Select = <T extends SelectItem>({
260
247
  loadMore={loadMore}
261
248
  searchQueryValue={searchQueryValue}
262
249
  onSearchQuery={onSearchQuery}
263
- style={
264
- (isMobile && {
265
- width: containerRef.current?.offsetWidth,
266
- transform: 'none',
267
- maxHeight: '250px',
268
- ...modalStyle,
269
- }) ||
270
- {}
271
- }
272
250
  searchQueryPlaceholder={searchQueryPlaceholder}
273
251
  options={options}
274
252
  selectOption={selectOption}
275
253
  unselectOption={unSelectOption}
276
- variant={optionVariant}
277
254
  />
278
255
  </SelectStyle>
279
256
  </Field>