@rettangoli/ui 0.1.2-rc9 → 0.1.4

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 (66) hide show
  1. package/dist/rettangoli-iife-layout.min.js +128 -56
  2. package/dist/rettangoli-iife-ui.min.js +187 -66
  3. package/package.json +3 -2
  4. package/src/common.js +30 -2
  5. package/src/components/breadcrumb/breadcrumb.handlers.js +10 -0
  6. package/src/components/breadcrumb/breadcrumb.store.js +29 -0
  7. package/src/components/breadcrumb/breadcrumb.view.yaml +64 -0
  8. package/src/components/dropdownMenu/dropdownMenu.handlers.js +7 -6
  9. package/src/components/dropdownMenu/dropdownMenu.store.js +6 -18
  10. package/src/components/dropdownMenu/dropdownMenu.view.yaml +15 -13
  11. package/src/components/form/form.handlers.js +181 -33
  12. package/src/components/form/form.store.js +175 -21
  13. package/src/components/form/form.view.yaml +182 -27
  14. package/src/components/globalUi/globalUi.handlers.js +53 -0
  15. package/src/components/globalUi/globalUi.store.js +57 -0
  16. package/src/components/globalUi/globalUi.view.yaml +50 -0
  17. package/src/components/navbar/navbar.handlers.js +2 -1
  18. package/src/components/navbar/navbar.store.js +2 -2
  19. package/src/components/pageOutline/pageOutline.handlers.js +57 -17
  20. package/src/components/pageOutline/pageOutline.store.js +48 -3
  21. package/src/components/pageOutline/pageOutline.view.yaml +7 -5
  22. package/src/components/popoverInput/popoverInput.handlers.js +103 -0
  23. package/src/components/popoverInput/popoverInput.store.js +48 -0
  24. package/src/components/popoverInput/popoverInput.view.yaml +55 -0
  25. package/src/components/select/select.handlers.js +124 -12
  26. package/src/components/select/select.store.js +86 -20
  27. package/src/components/select/select.view.yaml +40 -10
  28. package/src/components/sidebar/sidebar.handlers.js +8 -6
  29. package/src/components/sidebar/sidebar.store.js +6 -6
  30. package/src/components/sidebar/sidebar.view.yaml +1 -1
  31. package/src/components/sliderInput/sliderInput.handlers.js +24 -6
  32. package/src/components/sliderInput/sliderInput.store.js +3 -2
  33. package/src/components/sliderInput/sliderInput.view.yaml +3 -2
  34. package/src/components/table/table.handlers.js +12 -10
  35. package/src/components/table/table.store.js +4 -4
  36. package/src/components/tabs/tabs.handlers.js +11 -0
  37. package/src/components/tabs/tabs.store.js +29 -0
  38. package/src/components/tabs/tabs.view.yaml +64 -0
  39. package/src/components/tooltip/tooltip.handlers.js +0 -0
  40. package/src/components/tooltip/tooltip.store.js +12 -0
  41. package/src/components/tooltip/tooltip.view.yaml +27 -0
  42. package/src/components/waveform/waveform.handlers.js +92 -0
  43. package/src/components/waveform/waveform.store.js +17 -0
  44. package/src/components/waveform/waveform.view.yaml +38 -0
  45. package/src/deps/createGlobalUI.js +39 -0
  46. package/src/entry-iife-layout.js +3 -0
  47. package/src/entry-iife-ui.js +4 -0
  48. package/src/index.js +7 -1
  49. package/src/primitives/button.js +10 -0
  50. package/src/primitives/colorPicker.js +13 -4
  51. package/src/primitives/dialog.js +254 -0
  52. package/src/primitives/image.js +4 -3
  53. package/src/primitives/input.js +17 -4
  54. package/src/primitives/popover.js +280 -0
  55. package/src/primitives/slider.js +14 -4
  56. package/src/primitives/svg.js +2 -0
  57. package/src/primitives/textarea.js +25 -1
  58. package/src/primitives/view.js +132 -13
  59. package/src/setup.js +7 -2
  60. package/src/styles/cursorStyles.js +38 -2
  61. package/src/components/dialog/dialog.handlers.js +0 -5
  62. package/src/components/dialog/dialog.store.js +0 -25
  63. package/src/components/dialog/dialog.view.yaml +0 -48
  64. package/src/components/popover/popover.handlers.js +0 -5
  65. package/src/components/popover/popover.store.js +0 -12
  66. package/src/components/popover/popover.view.yaml +0 -57
@@ -3,9 +3,10 @@
3
3
  /**
4
4
  *
5
5
  * @param {*} headingElements
6
+ * @param {*} offsetTop
6
7
  * @param {*} deps
7
8
  */
8
- const updateToLatestCurrentId = (headingElements, deps) => {
9
+ const updateToLatestCurrentId = (headingElements, offsetTop, deps) => {
9
10
  const { store, render } = deps;
10
11
 
11
12
  let currentHeadingId;
@@ -14,7 +15,9 @@ const updateToLatestCurrentId = (headingElements, deps) => {
14
15
  headingElements.forEach((heading) => {
15
16
  const rect = heading.getBoundingClientRect();
16
17
 
17
- if (rect.top <= 20) {
18
+ // A heading is "current" if it's at or above the offset line
19
+ // We want the heading that's closest to the offset but still above it
20
+ if (rect.top <= offsetTop) {
18
21
  if (rect.top > closestTopPosition) {
19
22
  closestTopPosition = rect.top;
20
23
  currentHeadingId = heading.id;
@@ -22,46 +25,83 @@ const updateToLatestCurrentId = (headingElements, deps) => {
22
25
  }
23
26
  });
24
27
 
28
+ // If no heading is above the threshold, select the first visible heading below it
29
+ if (!currentHeadingId) {
30
+ let lowestTop = Infinity;
31
+ headingElements.forEach((heading) => {
32
+ const rect = heading.getBoundingClientRect();
33
+ if (rect.top > offsetTop && rect.top < lowestTop) {
34
+ lowestTop = rect.top;
35
+ currentHeadingId = heading.id;
36
+ }
37
+ });
38
+ }
39
+
25
40
  if (currentHeadingId && currentHeadingId !== store.selectCurrentId()) {
26
41
  store.setCurrentId(currentHeadingId);
27
42
  render();
28
43
  }
29
44
  };
30
45
 
31
- const startListening = (contentContainer, deps) => {
46
+ const startListening = (contentContainer, scrollContainer, offsetTop, deps) => {
32
47
  const { store, render } = deps;
33
48
 
34
49
  // Extract headings
35
- const headings = contentContainer.querySelectorAll("rtgl-text[id]");
50
+ const headings = contentContainer.querySelectorAll("h1[id], h2[id], h3[id], h4[id], rtgl-text[id]");
36
51
  const headingElements = Array.from(headings);
37
52
 
38
- const items = headingElements.map((heading) => ({
39
- id: heading.id,
40
- href: `#${heading.id}`,
41
- title: heading.textContent
42
- }));
53
+ const items = headingElements.map((heading) => {
54
+ let level = 1;
55
+ const tagName = heading.tagName.toLowerCase();
56
+
57
+ if (tagName === 'h1') level = 1;
58
+ else if (tagName === 'h2') level = 2;
59
+ else if (tagName === 'h3') level = 3;
60
+ else if (tagName === 'h4') level = 4;
61
+ else if (tagName === 'rtgl-text') {
62
+ // For rtgl-text, check if it has a data-level attribute or default to 1
63
+ level = parseInt(heading.getAttribute('data-level') || '1', 10);
64
+ }
65
+
66
+ return {
67
+ id: heading.id,
68
+ href: `#${heading.id}`,
69
+ title: heading.textContent,
70
+ level: level
71
+ };
72
+ });
43
73
 
44
74
  store.setItems(items);
45
- updateToLatestCurrentId(headingElements, deps);
75
+ updateToLatestCurrentId(headingElements, offsetTop, deps);
46
76
  render();
47
77
 
48
- const boundCheckCurrentHeading = updateToLatestCurrentId.bind(this, headingElements, deps);
78
+ const boundCheckCurrentHeading = updateToLatestCurrentId.bind(this, headingElements, offsetTop, deps);
49
79
 
50
- // Add scroll listener to the content container
51
- contentContainer.addEventListener("scroll", boundCheckCurrentHeading, {
80
+ // Add scroll listener to the scroll container
81
+ scrollContainer.addEventListener("scroll", boundCheckCurrentHeading, {
52
82
  passive: true,
53
83
  });
54
84
 
55
85
  return () => {
56
- contentContainer.removeEventListener("scroll", boundCheckCurrentHeading);
86
+ scrollContainer.removeEventListener("scroll", boundCheckCurrentHeading);
57
87
  }
58
88
  };
59
89
 
60
- export const handleOnMount = (deps) => {
90
+ export const handleBeforeMount = (deps) => {
61
91
  const { attrs } = deps;
62
92
  requestAnimationFrame(() => {
63
- const targetElement = document.getElementById(attrs['target-id'])
64
- const stopListening = startListening(targetElement, deps)
93
+ const targetElement = document.getElementById(attrs['target-id']);
94
+
95
+ // Get scroll container - default to window for page scroll if not specified
96
+ let scrollContainer = window;
97
+ if (attrs['scroll-container-id']) {
98
+ scrollContainer = document.getElementById(attrs['scroll-container-id']) || window;
99
+ }
100
+
101
+ // Get offset top - default to 100px if not specified
102
+ const offsetTop = parseInt(attrs['offset-top'] || '100', 10);
103
+
104
+ const stopListening = startListening(targetElement, scrollContainer, offsetTop, deps);
65
105
  return () => {
66
106
  stopListening();
67
107
  }
@@ -1,15 +1,60 @@
1
- export const INITIAL_STATE = Object.freeze({
1
+ export const createInitialState = () => Object.freeze({
2
2
  items: [],
3
3
  currentId: null,
4
4
  contentContainer: null
5
5
  });
6
6
 
7
- export const toViewData = ({ state }) => {
7
+ export const selectViewData = ({ state }) => {
8
+ // Find all parent IDs for the current active item
9
+ const getActiveParentIds = (items, currentId) => {
10
+ const activeParentIds = new Set();
11
+ const currentIndex = items.findIndex(item => item.id === currentId);
12
+
13
+ if (currentIndex === -1) return activeParentIds;
14
+
15
+ const currentLevel = items[currentIndex].level;
16
+
17
+ // Look backwards for all parents (items with lower level)
18
+ for (let i = currentIndex - 1; i >= 0; i--) {
19
+ if (items[i].level < currentLevel) {
20
+ // This is a parent - mark all ancestors
21
+ let ancestorLevel = items[i].level;
22
+ activeParentIds.add(items[i].id);
23
+
24
+ // Continue looking for grandparents
25
+ for (let j = i - 1; j >= 0; j--) {
26
+ if (items[j].level < ancestorLevel) {
27
+ activeParentIds.add(items[j].id);
28
+ ancestorLevel = items[j].level;
29
+ }
30
+ }
31
+ break; // Found the immediate parent chain
32
+ }
33
+ }
34
+
35
+ return activeParentIds;
36
+ };
37
+
38
+ const activeParentIds = getActiveParentIds(state.items, state.currentId);
39
+
8
40
  return {
9
41
  items: state.items.map((item) => {
42
+ const mlValues = {
43
+ 1: '0',
44
+ 2: '12px',
45
+ 3: '24px',
46
+ 4: '32px'
47
+ };
48
+
49
+ const isDirectlyActive = item.id === state.currentId;
50
+ const isParentActive = activeParentIds.has(item.id);
51
+ const active = isDirectlyActive || isParentActive;
52
+
10
53
  return {
11
54
  ...item,
12
- c: item.id === state.currentId ? 'fg' : 'mu-fg'
55
+ c: active ? 'fg' : 'mu-fg',
56
+ ml: mlValues[item.level] || '',
57
+ bc: active ? "fg" : "mu-fg"
13
58
  }
14
59
  }),
15
60
  currentId: state.currentId
@@ -27,8 +27,10 @@ events:
27
27
  type: object
28
28
 
29
29
  template:
30
- - rtgl-view h=f w=272:
31
- - rtgl-view w=f g=sm mt=xl:
32
- - $for item, i in items:
33
- - rtgl-view pv=xs av=c href=${item.href}:
34
- - rtgl-text s=sm c=${item.c} h-c=fg: ${item.title}
30
+ - rtgl-view h=f w=272 pr=md:
31
+ - rtgl-view w=f mt=xl:
32
+ - $for item, i in items:
33
+ - rtgl-view d=h bwl=xs bc="${item.bc}" pv=sm av=c href=${item.href} pl=md:
34
+ - rtgl-view w=${item.ml}:
35
+ - rtgl-text s=sm c=${item.c} h-c=fg: ${item.title}
36
+
@@ -0,0 +1,103 @@
1
+ export const handleBeforeMount = (deps) => {
2
+ const { store, props } = deps;
3
+
4
+ if (props.value !== undefined || props.defaultValue !== undefined) {
5
+ store.setValue(props.value || props.defaultValue || '');
6
+ }
7
+ }
8
+
9
+ export const handleOnUpdate = (deps, payload) => {
10
+ const { oldProps, newProps} = payload
11
+ const { store, props, render } = deps;
12
+
13
+ if (oldProps.defaultValue !== newProps.defaultValue) {
14
+ store.setValue(props.defaultValue || '');
15
+ }
16
+
17
+ render();
18
+ }
19
+
20
+ export const handleTextClick = (deps, payload) => {
21
+ const { store, render, getRefIds, attrs } = deps;
22
+ const event = payload._event;
23
+
24
+ const value = store.selectValue();
25
+ store.setTempValue(value)
26
+
27
+ store.openPopover({
28
+ position: {
29
+ x: event.currentTarget.getBoundingClientRect().left,
30
+ y: event.currentTarget.getBoundingClientRect().bottom,
31
+ }
32
+ });
33
+
34
+ const { input } = getRefIds();
35
+ input.elm.value = value;
36
+ render();
37
+
38
+ if (attrs['auto-focus']) {
39
+ setTimeout(() => {
40
+ input.elm.focus();
41
+ }, 50)
42
+ }
43
+ }
44
+
45
+ export const handlePopoverClose = (deps, payload) => {
46
+ const { store, render } = deps;
47
+ store.closePopover();
48
+ render();
49
+ }
50
+
51
+ export const handleInputChange = (deps, payload) => {
52
+ const { store, render, dispatchEvent } = deps;
53
+ const event = payload._event;
54
+ const value = event.detail.value;
55
+
56
+ store.setTempValue(value);
57
+
58
+ dispatchEvent(new CustomEvent('temp-input-change', {
59
+ detail: { value },
60
+ bubbles: true
61
+ }));
62
+
63
+ render();
64
+ }
65
+
66
+ export const handleSubmitClick = (deps, payload) => {
67
+ const { store, render, dispatchEvent, getRefIds } = deps;
68
+ const event = payload._event;
69
+ const { input } = getRefIds()
70
+ const value = input.elm.value;
71
+
72
+ store.setValue(value)
73
+ store.closePopover();
74
+
75
+ dispatchEvent(new CustomEvent('input-change', {
76
+ detail: { value },
77
+ bubbles: true
78
+ }));
79
+
80
+ render();
81
+ }
82
+
83
+ export const handleInputKeydown = (deps, payload) => {
84
+ const { store, render, dispatchEvent, getRefIds } = deps;
85
+ const event = payload._event;
86
+
87
+ if (event.key === 'Enter') {
88
+ const { input } = getRefIds()
89
+ const value = input.elm.value;
90
+
91
+ store.closePopover();
92
+ // Dispatch custom event
93
+ dispatchEvent(new CustomEvent('input-change', {
94
+ detail: { value },
95
+ bubbles: true
96
+ }));
97
+
98
+ render();
99
+ } else if (event.key === 'Escape') {
100
+ store.closePopover();
101
+ render();
102
+ }
103
+ }
@@ -0,0 +1,48 @@
1
+ export const createInitialState = () => Object.freeze({
2
+ isOpen: false,
3
+ position: {
4
+ x: 0,
5
+ y: 0,
6
+ },
7
+ value: '',
8
+ tempValue: '',
9
+ });
10
+
11
+ export const selectViewData = ({ attrs, state, props }) => {
12
+ // Use state's current value if it has been modified, otherwise use props
13
+ const value = state.value || '-';
14
+
15
+ return {
16
+ isOpen: state.isOpen,
17
+ position: state.position,
18
+ value: value ?? '-',
19
+ tempValue: state.tempValue,
20
+ placeholder: props.placeholder ?? '',
21
+ label: attrs.label,
22
+ };
23
+ }
24
+
25
+ export const setTempValue = (state, value) => {
26
+ state.tempValue = value;
27
+ }
28
+
29
+ export const openPopover = (state, payload) => {
30
+ const { position } = payload;
31
+ state.position = position;
32
+ state.isOpen = true;
33
+ // Reset the current value to match the display value when opening
34
+ state.hasUnsavedChanges = false;
35
+ }
36
+
37
+ export const closePopover = (state) => {
38
+ state.isOpen = false;
39
+ state.tempValue = '';
40
+ }
41
+
42
+ export const setValue = (state, value) => {
43
+ state.value = value;
44
+ }
45
+
46
+ export const selectValue = ({ state }) => {
47
+ return state.value;
48
+ }
@@ -0,0 +1,55 @@
1
+ elementName: rtgl-popover-input
2
+
3
+ viewDataSchema:
4
+ type: object
5
+
6
+ attrsSchema:
7
+ type: object
8
+ properties:
9
+ auto-focus:
10
+ type: boolean
11
+
12
+ propsSchema:
13
+ type: object
14
+ properties:
15
+ value:
16
+ type: string
17
+ defaultValue:
18
+ type: string
19
+ placeholder:
20
+ type: string
21
+ onChange:
22
+ type: function
23
+
24
+ refs:
25
+ text-display:
26
+ eventListeners:
27
+ click:
28
+ handler: handleTextClick
29
+ popover:
30
+ eventListeners:
31
+ close:
32
+ handler: handlePopoverClose
33
+ input:
34
+ eventListeners:
35
+ input-change:
36
+ handler: handleInputChange
37
+ keydown:
38
+ handler: handleInputKeydown
39
+ submit:
40
+ eventListeners:
41
+ click:
42
+ handler: handleSubmitClick
43
+
44
+ events:
45
+ input-change: {}
46
+
47
+ template:
48
+ - rtgl-view#text-display w=f cur=p:
49
+ - rtgl-text: ${value}
50
+ - rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y}:
51
+ - rtgl-view g=md w=240 slot=content bgc=background br=md:
52
+ - rtgl-text: ${label}
53
+ - rtgl-input#input w=f placeholder=${placeholder}:
54
+ - rtgl-view w=f ah=e:
55
+ - rtgl-button#submit: Submit
@@ -1,9 +1,10 @@
1
+ import { deepEqual } from '../../common.js';
1
2
 
2
- export const handleOnMount = (deps) => {
3
+ export const handleBeforeMount = (deps) => {
3
4
  const { store, props, render } = deps;
4
-
5
+
5
6
  if (props.selectedValue !== null && props.selectedValue !== undefined && props.options) {
6
- const selectedOption = props.options.find(opt => opt.value === props.selectedValue);
7
+ const selectedOption = props.options.find(opt => deepEqual(opt.value, props.selectedValue));
7
8
  if (selectedOption) {
8
9
  store.updateSelectOption(selectedOption);
9
10
  render();
@@ -11,26 +12,68 @@ export const handleOnMount = (deps) => {
11
12
  }
12
13
  }
13
14
 
14
- export const handleButtonClick = (e, deps) => {
15
- const { store, render, getRefIds } = deps;
15
+ export const handleOnUpdate = (deps, payload) => {
16
+ const { oldAttrs, newAttrs, oldProps, newProps } = payload;
17
+ const { store, props, render } = deps;
18
+
19
+ // Check if key changed
20
+ if (oldAttrs?.key !== newAttrs?.key && newAttrs?.key) {
21
+ // Clear current state using store action
22
+ store.resetSelection();
23
+
24
+ // Re-apply the prop value if available
25
+ const selectedValue = newProps?.selectedValue || props?.selectedValue;
26
+ const options = newProps?.options || props?.options;
27
+
28
+ if (selectedValue !== null && selectedValue !== undefined && options) {
29
+ const selectedOption = options.find(opt => deepEqual(opt.value, selectedValue));
30
+ if (selectedOption) {
31
+ store.updateSelectOption(selectedOption);
32
+ }
33
+ }
34
+ render();
35
+ }
36
+ }
37
+
38
+ export const handleButtonClick = (deps, payload) => {
39
+ const { store, render, getRefIds, props } = deps;
40
+ const event = payload._event;
41
+
42
+ const button = getRefIds()['select-button'].elm;
43
+
44
+ // Get first child's bounding rectangle (since button has display: contents)
45
+ const firstChild = button.firstElementChild;
46
+ const rect = firstChild ? firstChild.getBoundingClientRect() : button.getBoundingClientRect();
47
+
48
+ // Find the index of the currently selected option
49
+ const storeSelectedValue = store.selectSelectedValue();
50
+ const currentValue = storeSelectedValue !== null ? storeSelectedValue : props.selectedValue;
51
+ let selectedIndex = null;
52
+ if (currentValue !== null && currentValue !== undefined && props.options) {
53
+ selectedIndex = props.options.findIndex(opt => deepEqual(opt.value, currentValue));
54
+ if (selectedIndex === -1) selectedIndex = null;
55
+ }
56
+
16
57
  store.openOptionsPopover({
17
58
  position: {
18
- y: e.clientY,
19
- x: e.clientX,
20
- }
59
+ y: rect.bottom + 12, // Bottom edge of button
60
+ x: rect.left - 24, // Left edge of button
61
+ },
62
+ selectedIndex
21
63
  })
22
64
  render();
23
65
  }
24
66
 
25
- export const handleClickOptionsPopoverOverlay = (e, deps) => {
67
+ export const handleClickOptionsPopoverOverlay = (deps, payload) => {
26
68
  const { store, render } = deps;
27
69
  store.closeOptionsPopover();
28
70
  render();
29
71
  }
30
72
 
31
- export const handleOptionClick = (e, deps) => {
73
+ export const handleOptionClick = (deps, payload) => {
32
74
  const { render, dispatchEvent, props, store } = deps;
33
- const id = e.currentTarget.id.replace('option-', '');
75
+ const event = payload._event;
76
+ const id = event.currentTarget.id.replace('option-', '');
34
77
 
35
78
  const option = props.options[id];
36
79
 
@@ -53,6 +96,75 @@ export const handleOptionClick = (e, deps) => {
53
96
  detail: { selectedValue: option.value },
54
97
  bubbles: true
55
98
  }));
56
-
99
+
100
+ render();
101
+ }
102
+
103
+ export const handleOptionMouseEnter = (deps, payload) => {
104
+ const { store, render } = deps;
105
+ const event = payload._event;
106
+ const id = parseInt(event.currentTarget.id.replace('option-', ''));
107
+ store.setHoveredOption(id);
108
+ render();
109
+ }
110
+
111
+ export const handleOptionMouseLeave = (deps, payload) => {
112
+ const { store, render } = deps;
113
+ store.clearHoveredOption();
114
+ render();
115
+ }
116
+
117
+ export const handleClearClick = (deps, payload) => {
118
+ const { store, render, dispatchEvent, props } = deps;
119
+ const event = payload._event;
120
+
121
+ event.stopPropagation();
122
+
123
+ // Clear the internal state
124
+ store.clearSelectedValue();
125
+
126
+ // Call onChange if provided
127
+ if (props.onChange && typeof props.onChange === 'function') {
128
+ props.onChange(undefined);
129
+ }
130
+
131
+ // Dispatch custom event for backward compatibility
132
+ dispatchEvent(new CustomEvent('option-selected', {
133
+ detail: { value: undefined, label: undefined },
134
+ bubbles: true
135
+ }));
136
+
137
+ // Also dispatch select-change event to match form's event listener pattern
138
+ dispatchEvent(new CustomEvent('select-change', {
139
+ detail: { selectedValue: undefined },
140
+ bubbles: true
141
+ }));
142
+
143
+ render();
144
+ }
145
+
146
+ export const handleAddOptionClick = (deps, payload) => {
147
+ const { store, render, dispatchEvent } = deps;
148
+
149
+ // Close the popover
150
+ store.closeOptionsPopover();
151
+
152
+ // Dispatch custom event for add option (no detail)
153
+ dispatchEvent(new CustomEvent('add-option-selected', {
154
+ bubbles: true
155
+ }));
156
+
157
+ render();
158
+ }
159
+
160
+ export const handleAddOptionMouseEnter = (deps, payload) => {
161
+ const { store, render } = deps;
162
+ store.setHoveredAddOption(true);
163
+ render();
164
+ }
165
+
166
+ export const handleAddOptionMouseLeave = (deps, payload) => {
167
+ const { store, render } = deps;
168
+ store.setHoveredAddOption(false);
57
169
  render();
58
170
  }