shadcn_phlexcomponents 0.1.18 → 0.1.19

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. checksums.yaml +4 -4
  2. data/README.md +321 -23
  3. data/app/javascript/controllers/accordion_controller.js +101 -90
  4. data/app/javascript/controllers/alert_dialog_controller.js +5 -4
  5. data/app/javascript/controllers/avatar_controller.js +12 -11
  6. data/app/javascript/controllers/checkbox_controller.js +26 -26
  7. data/app/javascript/controllers/collapsible_controller.js +35 -36
  8. data/app/javascript/controllers/combobox_controller.js +262 -231
  9. data/app/javascript/controllers/command_controller.js +205 -184
  10. data/app/javascript/controllers/date_picker_controller.js +252 -253
  11. data/app/javascript/controllers/date_range_picker_controller.js +189 -200
  12. data/app/javascript/controllers/dialog_controller.js +79 -78
  13. data/app/javascript/controllers/dropdown_menu_controller.js +229 -208
  14. data/app/javascript/controllers/dropdown_menu_sub_controller.js +111 -97
  15. data/app/javascript/controllers/form_field_controller.js +17 -16
  16. data/app/javascript/controllers/hover_card_controller.js +69 -71
  17. data/app/javascript/controllers/loading_button_controller.js +11 -10
  18. data/app/javascript/controllers/popover_controller.js +85 -78
  19. data/app/javascript/controllers/progress_controller.js +12 -11
  20. data/app/javascript/controllers/radio_group_controller.js +75 -74
  21. data/app/javascript/controllers/select_controller.js +247 -232
  22. data/app/javascript/controllers/sidebar_controller.js +26 -27
  23. data/app/javascript/controllers/sidebar_trigger_controller.js +12 -9
  24. data/app/javascript/controllers/slider_controller.js +74 -74
  25. data/app/javascript/controllers/switch_controller.js +23 -23
  26. data/app/javascript/controllers/tabs_controller.js +61 -61
  27. data/app/javascript/controllers/theme_switcher_controller.js +28 -27
  28. data/app/javascript/controllers/toast_container_controller.js +45 -31
  29. data/app/javascript/controllers/toast_controller.js +19 -18
  30. data/app/javascript/controllers/toggle_controller.js +17 -17
  31. data/app/javascript/controllers/toggle_group_controller.js +17 -17
  32. data/app/javascript/controllers/tooltip_controller.js +75 -77
  33. data/app/javascript/shadcn_phlexcomponents.js +27 -60
  34. data/app/javascript/utils/command.js +390 -334
  35. data/app/javascript/utils/floating_ui.js +139 -107
  36. data/app/javascript/utils/index.js +253 -190
  37. data/app/typescript/controllers/accordion_controller.ts +2 -0
  38. data/app/typescript/controllers/alert_dialog_controller.ts +2 -0
  39. data/app/typescript/controllers/avatar_controller.ts +2 -0
  40. data/app/typescript/controllers/checkbox_controller.ts +2 -0
  41. data/app/typescript/controllers/collapsible_controller.ts +2 -0
  42. data/app/typescript/controllers/combobox_controller.ts +2 -0
  43. data/app/typescript/controllers/command_controller.ts +2 -0
  44. data/app/typescript/controllers/date_picker_controller.ts +2 -0
  45. data/app/typescript/controllers/date_range_picker_controller.ts +2 -0
  46. data/app/typescript/controllers/dialog_controller.ts +2 -0
  47. data/app/typescript/controllers/dropdown_menu_controller.ts +2 -0
  48. data/app/typescript/controllers/dropdown_menu_sub_controller.ts +2 -0
  49. data/app/typescript/controllers/form_field_controller.ts +2 -0
  50. data/app/typescript/controllers/hover_card_controller.ts +2 -0
  51. data/app/typescript/controllers/loading_button_controller.ts +2 -0
  52. data/app/typescript/controllers/popover_controller.ts +2 -0
  53. data/app/typescript/controllers/progress_controller.ts +2 -0
  54. data/app/typescript/controllers/radio_group_controller.ts +2 -0
  55. data/app/typescript/controllers/select_controller.ts +2 -0
  56. data/app/typescript/controllers/slider_controller.ts +2 -0
  57. data/app/typescript/controllers/switch_controller.ts +2 -0
  58. data/app/typescript/controllers/tabs_controller.ts +2 -0
  59. data/app/typescript/controllers/theme_switcher_controller.ts +2 -0
  60. data/app/typescript/controllers/toast_container_controller.ts +2 -0
  61. data/app/typescript/controllers/toast_controller.ts +2 -0
  62. data/app/typescript/controllers/toggle_controller.ts +2 -0
  63. data/app/typescript/controllers/toggle_group_controller.ts +2 -0
  64. data/app/typescript/controllers/tooltip_controller.ts +2 -0
  65. data/app/typescript/shadcn_phlexcomponents.ts +27 -61
  66. data/app/typescript/utils/index.ts +7 -0
  67. data/lib/install/upgrade_shadcn_phlexcomponents.rb +28 -0
  68. data/lib/shadcn_phlexcomponents/components/accordion.rb +55 -12
  69. data/lib/shadcn_phlexcomponents/components/alert.rb +35 -16
  70. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +52 -12
  71. data/lib/shadcn_phlexcomponents/components/aspect_ratio.rb +33 -2
  72. data/lib/shadcn_phlexcomponents/components/avatar.rb +24 -7
  73. data/lib/shadcn_phlexcomponents/components/badge.rb +23 -18
  74. data/lib/shadcn_phlexcomponents/components/breadcrumb.rb +46 -6
  75. data/lib/shadcn_phlexcomponents/components/button.rb +32 -27
  76. data/lib/shadcn_phlexcomponents/components/card.rb +59 -10
  77. data/lib/shadcn_phlexcomponents/components/checkbox.rb +51 -30
  78. data/lib/shadcn_phlexcomponents/components/checkbox_group.rb +24 -4
  79. data/lib/shadcn_phlexcomponents/components/combobox.rb +212 -69
  80. data/lib/shadcn_phlexcomponents/components/command.rb +156 -52
  81. data/lib/shadcn_phlexcomponents/components/date_picker.rb +134 -48
  82. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +20 -42
  83. data/lib/shadcn_phlexcomponents/components/dialog.rb +80 -26
  84. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +74 -25
  85. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +52 -24
  86. data/lib/shadcn_phlexcomponents/components/form/form_checkbox.rb +1 -1
  87. data/lib/shadcn_phlexcomponents/components/form/form_checkbox_group.rb +1 -1
  88. data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +1 -1
  89. data/lib/shadcn_phlexcomponents/components/form/form_date_picker.rb +1 -1
  90. data/lib/shadcn_phlexcomponents/components/form/form_date_range_picker.rb +1 -1
  91. data/lib/shadcn_phlexcomponents/components/form/form_error.rb +8 -1
  92. data/lib/shadcn_phlexcomponents/components/form/form_helpers.rb +3 -2
  93. data/lib/shadcn_phlexcomponents/components/form/form_hint.rb +8 -1
  94. data/lib/shadcn_phlexcomponents/components/form/form_input.rb +1 -1
  95. data/lib/shadcn_phlexcomponents/components/form/form_radio_group.rb +1 -1
  96. data/lib/shadcn_phlexcomponents/components/form/form_select.rb +1 -1
  97. data/lib/shadcn_phlexcomponents/components/form/form_slider.rb +1 -1
  98. data/lib/shadcn_phlexcomponents/components/form/form_switch.rb +1 -1
  99. data/lib/shadcn_phlexcomponents/components/form/form_textarea.rb +1 -1
  100. data/lib/shadcn_phlexcomponents/components/form.rb +22 -6
  101. data/lib/shadcn_phlexcomponents/components/hover_card.rb +48 -18
  102. data/lib/shadcn_phlexcomponents/components/input.rb +13 -8
  103. data/lib/shadcn_phlexcomponents/components/label.rb +9 -4
  104. data/lib/shadcn_phlexcomponents/components/link.rb +8 -1
  105. data/lib/shadcn_phlexcomponents/components/pagination.rb +34 -6
  106. data/lib/shadcn_phlexcomponents/components/popover.rb +43 -13
  107. data/lib/shadcn_phlexcomponents/components/progress.rb +37 -6
  108. data/lib/shadcn_phlexcomponents/components/radio_group.rb +41 -15
  109. data/lib/shadcn_phlexcomponents/components/select.rb +99 -42
  110. data/lib/shadcn_phlexcomponents/components/separator.rb +9 -4
  111. data/lib/shadcn_phlexcomponents/components/sheet.rb +87 -21
  112. data/lib/shadcn_phlexcomponents/components/skeleton.rb +8 -1
  113. data/lib/shadcn_phlexcomponents/components/switch.rb +45 -17
  114. data/lib/shadcn_phlexcomponents/components/table.rb +84 -17
  115. data/lib/shadcn_phlexcomponents/components/tabs.rb +36 -12
  116. data/lib/shadcn_phlexcomponents/components/textarea.rb +12 -7
  117. data/lib/shadcn_phlexcomponents/components/toast.rb +46 -20
  118. data/lib/shadcn_phlexcomponents/components/toast_container.rb +19 -14
  119. data/lib/shadcn_phlexcomponents/components/toggle.rb +28 -23
  120. data/lib/shadcn_phlexcomponents/components/tooltip.rb +49 -14
  121. data/lib/shadcn_phlexcomponents/configuration.rb +46 -0
  122. data/lib/shadcn_phlexcomponents/initializers/shadcn_phlexcomponents.rb +28 -0
  123. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  124. data/lib/shadcn_phlexcomponents.rb +12 -1
  125. data/lib/tasks/upgrade.rake +10 -0
  126. metadata +15 -14
  127. data/app/typescript/controllers/sidebar_controller.ts +0 -39
  128. data/app/typescript/controllers/sidebar_trigger_controller.ts +0 -21
@@ -1,218 +1,239 @@
1
- import { Controller } from '@hotwired/stimulus';
2
- import { useClickOutside } from 'stimulus-use';
3
- import { initFloatingUi } from '../utils/floating_ui';
4
- import { getSameLevelItems, focusTrigger, hideContent, showContent, lockScroll, unlockScroll, getStimulusInstance, onClickOutside, getNextEnabledIndex, getPreviousEnabledIndex, focusElement, } from '../utils';
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import { useClickOutside } from "stimulus-use";
3
+ import { initFloatingUi } from "../utils/floating_ui";
4
+ import {
5
+ getSameLevelItems,
6
+ focusTrigger,
7
+ hideContent,
8
+ showContent,
9
+ lockScroll,
10
+ unlockScroll,
11
+ getStimulusInstance,
12
+ onClickOutside,
13
+ getNextEnabledIndex,
14
+ getPreviousEnabledIndex,
15
+ focusElement,
16
+ } from "../utils";
5
17
  const onKeydown = (controller, event) => {
6
- const key = event.key;
7
- if (['Tab', 'Enter', ' '].includes(key))
8
- event.preventDefault();
9
- if (key === 'Home') {
10
- controller.focusItemByIndex(null, 0);
11
- }
12
- else if (key === 'End') {
13
- controller.focusItemByIndex(null, controller.items.length - 1);
14
- }
15
- else if (key === 'Escape') {
16
- controller.close();
17
- }
18
+ const key = event.key;
19
+ if (["Tab", "Enter", " "].includes(key)) event.preventDefault();
20
+ if (key === "Home") {
21
+ controller.focusItemByIndex(null, 0);
22
+ } else if (key === "End") {
23
+ controller.focusItemByIndex(null, controller.items.length - 1);
24
+ } else if (key === "Escape") {
25
+ controller.close();
26
+ }
18
27
  };
19
28
  const focusItemByIndex = (controller, event = null, index = null) => {
20
- if (event !== null) {
21
- const key = event.key;
22
- if (key === 'ArrowUp') {
23
- controller.items[controller.items.length - 1].focus();
24
- }
25
- else {
26
- controller.items[0].focus();
27
- }
28
- }
29
- else if (index !== null) {
30
- controller.items[index].focus();
31
- }
29
+ if (event !== null) {
30
+ const key = event.key;
31
+ if (key === "ArrowUp") {
32
+ controller.items[controller.items.length - 1].focus();
33
+ } else {
34
+ controller.items[0].focus();
35
+ }
36
+ } else if (index !== null) {
37
+ controller.items[index].focus();
38
+ }
32
39
  };
33
40
  const DropdownMenuController = class extends Controller {
34
- // targets
35
- static targets = ['trigger', 'contentContainer', 'content', 'item'];
36
- // values
37
- static values = {
38
- isOpen: Boolean,
39
- };
40
- connect() {
41
- this.closestContentSelector =
42
- '[data-dropdown-menu-target="content"], [data-dropdown-menu-sub-target="content"]';
43
- this.items = getSameLevelItems({
44
- content: this.contentTarget,
45
- items: this.itemTargets,
46
- closestContentSelector: this.closestContentSelector,
47
- });
48
- useClickOutside(this, { element: this.contentTarget, dispatchEvent: false });
49
- this.DOMKeydownListener = this.onDOMKeydown.bind(this);
50
- }
51
- toggle(event) {
52
- if (this.isOpenValue) {
53
- this.close();
54
- }
55
- else {
56
- this.open(event);
57
- }
58
- }
59
- open(event) {
60
- this.isOpenValue = true;
61
- // Sub menus are not connected to the DOM yet when dropdown menu is connected.
62
- // So we initialize them here instead of in connect().
63
- if (this.subMenuControllers === undefined) {
64
- const subMenuControllers = [];
65
- const subMenus = Array.from(this.contentTarget.querySelectorAll('[data-shadcn-phlexcomponents="dropdown-menu-sub"]'));
66
- subMenus.forEach((subMenu) => {
67
- const subMenuController = getStimulusInstance('dropdown-menu-sub', subMenu);
68
- if (subMenuController) {
69
- subMenuControllers.push(subMenuController);
70
- }
71
- });
72
- this.subMenuControllers = subMenuControllers;
73
- }
74
- let elementToFocus = null;
75
- if (event instanceof KeyboardEvent) {
76
- const key = event.key;
77
- if (['ArrowDown', 'Enter', ' '].includes(key)) {
78
- elementToFocus = this.items[0];
79
- }
41
+ static name = "dropdown-menu";
42
+ // targets
43
+ static targets = ["trigger", "contentContainer", "content", "item"];
44
+ // values
45
+ static values = {
46
+ isOpen: Boolean,
47
+ };
48
+ connect() {
49
+ this.closestContentSelector =
50
+ '[data-dropdown-menu-target="content"], [data-dropdown-menu-sub-target="content"]';
51
+ this.items = getSameLevelItems({
52
+ content: this.contentTarget,
53
+ items: this.itemTargets,
54
+ closestContentSelector: this.closestContentSelector,
55
+ });
56
+ useClickOutside(this, {
57
+ element: this.contentTarget,
58
+ dispatchEvent: false,
59
+ });
60
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this);
61
+ }
62
+ toggle(event) {
63
+ if (this.isOpenValue) {
64
+ this.close();
65
+ } else {
66
+ this.open(event);
67
+ }
68
+ }
69
+ open(event) {
70
+ this.isOpenValue = true;
71
+ // Sub menus are not connected to the DOM yet when dropdown menu is connected.
72
+ // So we initialize them here instead of in connect().
73
+ if (this.subMenuControllers === undefined) {
74
+ const subMenuControllers = [];
75
+ const subMenus = Array.from(
76
+ this.contentTarget.querySelectorAll(
77
+ '[data-shadcn-phlexcomponents="dropdown-menu-sub"]',
78
+ ),
79
+ );
80
+ subMenus.forEach((subMenu) => {
81
+ const subMenuController = getStimulusInstance(
82
+ "dropdown-menu-sub",
83
+ subMenu,
84
+ );
85
+ if (subMenuController) {
86
+ subMenuControllers.push(subMenuController);
80
87
  }
81
- else {
82
- elementToFocus = this.contentTarget;
83
- }
84
- focusElement(elementToFocus);
85
- }
86
- close() {
87
- this.isOpenValue = false;
88
- this.subMenuControllers.forEach((subMenuController) => {
89
- subMenuController.closeImmediately();
88
+ });
89
+ this.subMenuControllers = subMenuControllers;
90
+ }
91
+ let elementToFocus = null;
92
+ if (event instanceof KeyboardEvent) {
93
+ const key = event.key;
94
+ if (["ArrowDown", "Enter", " "].includes(key)) {
95
+ elementToFocus = this.items[0];
96
+ }
97
+ } else {
98
+ elementToFocus = this.contentTarget;
99
+ }
100
+ focusElement(elementToFocus);
101
+ }
102
+ close() {
103
+ this.isOpenValue = false;
104
+ this.subMenuControllers.forEach((subMenuController) => {
105
+ subMenuController.closeImmediately();
106
+ });
107
+ }
108
+ focusItem(event) {
109
+ const item = event.currentTarget;
110
+ let items = [];
111
+ const content = item.closest(this.closestContentSelector);
112
+ const isSubMenu =
113
+ content.dataset.shadcnPhlexcomponents === "dropdown-menu-sub-content";
114
+ if (isSubMenu) {
115
+ const subMenu = content.closest(
116
+ '[data-shadcn-phlexcomponents="dropdown-menu-sub"]',
117
+ );
118
+ const subMenuController = this.subMenuControllers.find(
119
+ (subMenuController) => subMenuController.element == subMenu,
120
+ );
121
+ if (subMenuController) {
122
+ items = subMenuController.items;
123
+ }
124
+ } else {
125
+ items = this.items;
126
+ }
127
+ const index = items.indexOf(item);
128
+ if (event instanceof KeyboardEvent) {
129
+ const key = event.key;
130
+ let newIndex = 0;
131
+ if (key === "ArrowUp") {
132
+ newIndex = getPreviousEnabledIndex({
133
+ items,
134
+ currentIndex: index,
135
+ wrapAround: false,
90
136
  });
91
- }
92
- focusItem(event) {
93
- const item = event.currentTarget;
94
- let items = [];
95
- const content = item.closest(this.closestContentSelector);
96
- const isSubMenu = content.dataset.shadcnPhlexcomponents === 'dropdown-menu-sub-content';
97
- if (isSubMenu) {
98
- const subMenu = content.closest('[data-shadcn-phlexcomponents="dropdown-menu-sub"]');
99
- const subMenuController = this.subMenuControllers.find((subMenuController) => subMenuController.element == subMenu);
100
- if (subMenuController) {
101
- items = subMenuController.items;
102
- }
103
- }
104
- else {
105
- items = this.items;
106
- }
107
- const index = items.indexOf(item);
108
- if (event instanceof KeyboardEvent) {
109
- const key = event.key;
110
- let newIndex = 0;
111
- if (key === 'ArrowUp') {
112
- newIndex = getPreviousEnabledIndex({
113
- items,
114
- currentIndex: index,
115
- wrapAround: false,
116
- });
117
- }
118
- else {
119
- newIndex = getNextEnabledIndex({
120
- items,
121
- currentIndex: index,
122
- wrapAround: false,
123
- });
124
- }
125
- items[newIndex].focus();
126
- }
127
- else {
128
- // item mouseover event
129
- items[index].focus();
130
- }
131
- // Close submenus on the same level
132
- const subMenusInContent = Array.from(content.querySelectorAll('[data-shadcn-phlexcomponents="dropdown-menu-sub"]'));
133
- subMenusInContent.forEach((subMenu) => {
134
- const subMenuController = this.subMenuControllers.find((subMenuController) => subMenuController.element == subMenu);
135
- if (subMenuController) {
136
- subMenuController.closeImmediately();
137
- }
137
+ } else {
138
+ newIndex = getNextEnabledIndex({
139
+ items,
140
+ currentIndex: index,
141
+ wrapAround: false,
138
142
  });
139
- }
140
- onItemFocus(event) {
141
- const item = event.currentTarget;
142
- item.tabIndex = 0;
143
- }
144
- onItemBlur(event) {
145
- const item = event.currentTarget;
146
- item.tabIndex = -1;
147
- }
148
- focusItemByIndex(event = null, index = null) {
149
- focusItemByIndex(this, event, index);
150
- }
151
- focusContent(event) {
152
- const item = event.currentTarget;
153
- const content = item.closest(this.closestContentSelector);
154
- content.focus();
155
- }
156
- select(event) {
157
- if (event instanceof KeyboardEvent) {
158
- const key = event.key;
159
- const item = (event.currentTarget || event.target);
160
- // For rails button_to
161
- if (item && (key === 'Enter' || key === ' ')) {
162
- item.click();
163
- }
164
- }
165
- this.close();
166
- }
167
- clickOutside(event) {
168
- onClickOutside(this, event);
169
- }
170
- isOpenValueChanged(isOpen, previousIsOpen) {
171
- if (isOpen) {
172
- lockScroll(this.contentTarget.id);
173
- showContent({
174
- trigger: this.triggerTarget,
175
- content: this.contentTarget,
176
- contentContainer: this.contentContainerTarget,
177
- setEqualWidth: false,
178
- });
179
- this.cleanup = initFloatingUi({
180
- referenceElement: this.triggerTarget,
181
- floatingElement: this.contentContainerTarget,
182
- side: this.contentTarget.dataset.side,
183
- align: this.contentTarget.dataset.align,
184
- sideOffset: 4,
185
- });
186
- this.setupEventListeners();
187
- }
188
- else {
189
- unlockScroll(this.contentTarget.id);
190
- hideContent({
191
- trigger: this.triggerTarget,
192
- content: this.contentTarget,
193
- contentContainer: this.contentContainerTarget,
194
- });
195
- if (previousIsOpen) {
196
- focusTrigger(this.triggerTarget);
197
- }
198
- this.cleanupEventListeners();
199
- }
200
- }
201
- disconnect() {
202
- this.cleanupEventListeners();
203
- }
204
- onDOMKeydown(event) {
205
- if (!this.isOpenValue)
206
- return;
207
- onKeydown(this, event);
208
- }
209
- setupEventListeners() {
210
- document.addEventListener('keydown', this.DOMKeydownListener);
211
- }
212
- cleanupEventListeners() {
213
- if (this.cleanup)
214
- this.cleanup();
215
- document.removeEventListener('keydown', this.DOMKeydownListener);
216
- }
143
+ }
144
+ items[newIndex].focus();
145
+ } else {
146
+ // item mouseover event
147
+ items[index].focus();
148
+ }
149
+ // Close submenus on the same level
150
+ const subMenusInContent = Array.from(
151
+ content.querySelectorAll(
152
+ '[data-shadcn-phlexcomponents="dropdown-menu-sub"]',
153
+ ),
154
+ );
155
+ subMenusInContent.forEach((subMenu) => {
156
+ const subMenuController = this.subMenuControllers.find(
157
+ (subMenuController) => subMenuController.element == subMenu,
158
+ );
159
+ if (subMenuController) {
160
+ subMenuController.closeImmediately();
161
+ }
162
+ });
163
+ }
164
+ onItemFocus(event) {
165
+ const item = event.currentTarget;
166
+ item.tabIndex = 0;
167
+ }
168
+ onItemBlur(event) {
169
+ const item = event.currentTarget;
170
+ item.tabIndex = -1;
171
+ }
172
+ focusItemByIndex(event = null, index = null) {
173
+ focusItemByIndex(this, event, index);
174
+ }
175
+ focusContent(event) {
176
+ const item = event.currentTarget;
177
+ const content = item.closest(this.closestContentSelector);
178
+ content.focus();
179
+ }
180
+ select(event) {
181
+ if (event instanceof KeyboardEvent) {
182
+ const key = event.key;
183
+ const item = event.currentTarget || event.target;
184
+ // For rails button_to
185
+ if (item && (key === "Enter" || key === " ")) {
186
+ item.click();
187
+ }
188
+ }
189
+ this.close();
190
+ }
191
+ clickOutside(event) {
192
+ onClickOutside(this, event);
193
+ }
194
+ isOpenValueChanged(isOpen, previousIsOpen) {
195
+ if (isOpen) {
196
+ lockScroll(this.contentTarget.id);
197
+ showContent({
198
+ trigger: this.triggerTarget,
199
+ content: this.contentTarget,
200
+ contentContainer: this.contentContainerTarget,
201
+ setEqualWidth: false,
202
+ });
203
+ this.cleanup = initFloatingUi({
204
+ referenceElement: this.triggerTarget,
205
+ floatingElement: this.contentContainerTarget,
206
+ side: this.contentTarget.dataset.side,
207
+ align: this.contentTarget.dataset.align,
208
+ sideOffset: 4,
209
+ });
210
+ this.setupEventListeners();
211
+ } else {
212
+ unlockScroll(this.contentTarget.id);
213
+ hideContent({
214
+ trigger: this.triggerTarget,
215
+ content: this.contentTarget,
216
+ contentContainer: this.contentContainerTarget,
217
+ });
218
+ if (previousIsOpen) {
219
+ focusTrigger(this.triggerTarget);
220
+ }
221
+ this.cleanupEventListeners();
222
+ }
223
+ }
224
+ disconnect() {
225
+ this.cleanupEventListeners();
226
+ }
227
+ onDOMKeydown(event) {
228
+ if (!this.isOpenValue) return;
229
+ onKeydown(this, event);
230
+ }
231
+ setupEventListeners() {
232
+ document.addEventListener("keydown", this.DOMKeydownListener);
233
+ }
234
+ cleanupEventListeners() {
235
+ if (this.cleanup) this.cleanup();
236
+ document.removeEventListener("keydown", this.DOMKeydownListener);
237
+ }
217
238
  };
218
239
  export { DropdownMenuController, onKeydown, focusItemByIndex };
@@ -1,105 +1,119 @@
1
- import { Controller } from '@hotwired/stimulus';
2
- import { initFloatingUi } from '../utils/floating_ui';
3
- import { ON_OPEN_FOCUS_DELAY, getSameLevelItems, showContent, hideContent, getStimulusInstance, } from '../utils';
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import { initFloatingUi } from "../utils/floating_ui";
3
+ import {
4
+ ON_OPEN_FOCUS_DELAY,
5
+ getSameLevelItems,
6
+ showContent,
7
+ hideContent,
8
+ getStimulusInstance,
9
+ } from "../utils";
4
10
  const DropdownMenuSubController = class extends Controller {
5
- // targets
6
- static targets = ['trigger', 'contentContainer', 'content'];
7
- // values
8
- static values = {
9
- isOpen: Boolean,
10
- };
11
- connect() {
12
- this.items = getSameLevelItems({
13
- content: this.contentTarget,
14
- items: Array.from(this.contentTarget.querySelectorAll('[data-dropdown-menu-target="item"], [data-dropdown-menu-sub-target="trigger"]')),
15
- closestContentSelector: '[data-dropdown-menu-sub-target="content"]',
16
- });
17
- }
18
- open(event = null) {
19
- clearTimeout(this.closeTimeout);
20
- this.isOpenValue = true;
21
- setTimeout(() => {
22
- if (event instanceof KeyboardEvent) {
23
- const key = event.key;
24
- if (['ArrowRight', 'Enter', ' '].includes(key)) {
25
- this.focusItemByIndex(null, 0);
26
- }
27
- }
28
- }, ON_OPEN_FOCUS_DELAY);
29
- }
30
- close() {
31
- this.closeTimeout = window.setTimeout(() => {
32
- this.isOpenValue = false;
33
- }, 250);
34
- }
35
- closeOnLeftKeydown() {
36
- this.closeImmediately();
37
- this.triggerTarget.focus();
38
- }
39
- focusItemByIndex(event, index) {
40
- if (event) {
41
- const key = event.key;
42
- if (key === 'ArrowUp') {
43
- this.items[this.items.length - 1].focus();
44
- }
45
- else {
46
- this.items[0].focus();
47
- }
48
- }
49
- else if (index !== null) {
50
- this.items[index].focus();
11
+ static name = "dropdown-menu-sub";
12
+ // targets
13
+ static targets = ["trigger", "contentContainer", "content"];
14
+ // values
15
+ static values = {
16
+ isOpen: Boolean,
17
+ };
18
+ connect() {
19
+ this.items = getSameLevelItems({
20
+ content: this.contentTarget,
21
+ items: Array.from(
22
+ this.contentTarget.querySelectorAll(
23
+ '[data-dropdown-menu-target="item"], [data-dropdown-menu-sub-target="trigger"]',
24
+ ),
25
+ ),
26
+ closestContentSelector: '[data-dropdown-menu-sub-target="content"]',
27
+ });
28
+ }
29
+ open(event = null) {
30
+ clearTimeout(this.closeTimeout);
31
+ this.isOpenValue = true;
32
+ setTimeout(() => {
33
+ if (event instanceof KeyboardEvent) {
34
+ const key = event.key;
35
+ if (["ArrowRight", "Enter", " "].includes(key)) {
36
+ this.focusItemByIndex(null, 0);
51
37
  }
38
+ }
39
+ }, ON_OPEN_FOCUS_DELAY);
40
+ }
41
+ close() {
42
+ this.closeTimeout = window.setTimeout(() => {
43
+ this.isOpenValue = false;
44
+ }, 250);
45
+ }
46
+ closeOnLeftKeydown() {
47
+ this.closeImmediately();
48
+ this.triggerTarget.focus();
49
+ }
50
+ focusItemByIndex(event, index) {
51
+ if (event) {
52
+ const key = event.key;
53
+ if (key === "ArrowUp") {
54
+ this.items[this.items.length - 1].focus();
55
+ } else {
56
+ this.items[0].focus();
57
+ }
58
+ } else if (index !== null) {
59
+ this.items[index].focus();
52
60
  }
53
- closeParentSubMenu() {
54
- const parentContent = this.triggerTarget.closest('[data-dropdown-menu-sub-target="content"]');
55
- if (parentContent) {
56
- const subMenu = parentContent.closest('[data-shadcn-phlexcomponents="dropdown-menu-sub"]');
57
- if (subMenu) {
58
- const subMenuController = getStimulusInstance('dropdown-menu-sub', subMenu);
59
- if (subMenuController) {
60
- subMenuController.closeImmediately();
61
- setTimeout(() => {
62
- subMenuController.triggerTarget.focus();
63
- }, 100);
64
- }
65
- }
61
+ }
62
+ closeParentSubMenu() {
63
+ const parentContent = this.triggerTarget.closest(
64
+ '[data-dropdown-menu-sub-target="content"]',
65
+ );
66
+ if (parentContent) {
67
+ const subMenu = parentContent.closest(
68
+ '[data-shadcn-phlexcomponents="dropdown-menu-sub"]',
69
+ );
70
+ if (subMenu) {
71
+ const subMenuController = getStimulusInstance(
72
+ "dropdown-menu-sub",
73
+ subMenu,
74
+ );
75
+ if (subMenuController) {
76
+ subMenuController.closeImmediately();
77
+ setTimeout(() => {
78
+ subMenuController.triggerTarget.focus();
79
+ }, 100);
66
80
  }
81
+ }
67
82
  }
68
- closeImmediately() {
69
- this.isOpenValue = false;
70
- }
71
- isOpenValueChanged(isOpen) {
72
- if (isOpen) {
73
- showContent({
74
- trigger: this.triggerTarget,
75
- content: this.contentTarget,
76
- contentContainer: this.contentContainerTarget,
77
- });
78
- this.cleanup = initFloatingUi({
79
- referenceElement: this.triggerTarget,
80
- floatingElement: this.contentContainerTarget,
81
- side: this.contentTarget.dataset.side,
82
- align: this.contentTarget.dataset.align,
83
- sideOffset: -2,
84
- });
85
- }
86
- else {
87
- this.closeTimeout = window.setTimeout(() => {
88
- hideContent({
89
- trigger: this.triggerTarget,
90
- content: this.contentTarget,
91
- contentContainer: this.contentContainerTarget,
92
- });
93
- });
94
- this.cleanupEventListeners();
95
- }
96
- }
97
- disconnect() {
98
- this.cleanupEventListeners();
99
- }
100
- cleanupEventListeners() {
101
- if (this.cleanup)
102
- this.cleanup();
83
+ }
84
+ closeImmediately() {
85
+ this.isOpenValue = false;
86
+ }
87
+ isOpenValueChanged(isOpen) {
88
+ if (isOpen) {
89
+ showContent({
90
+ trigger: this.triggerTarget,
91
+ content: this.contentTarget,
92
+ contentContainer: this.contentContainerTarget,
93
+ });
94
+ this.cleanup = initFloatingUi({
95
+ referenceElement: this.triggerTarget,
96
+ floatingElement: this.contentContainerTarget,
97
+ side: this.contentTarget.dataset.side,
98
+ align: this.contentTarget.dataset.align,
99
+ sideOffset: -2,
100
+ });
101
+ } else {
102
+ this.closeTimeout = window.setTimeout(() => {
103
+ hideContent({
104
+ trigger: this.triggerTarget,
105
+ content: this.contentTarget,
106
+ contentContainer: this.contentContainerTarget,
107
+ });
108
+ });
109
+ this.cleanupEventListeners();
103
110
  }
111
+ }
112
+ disconnect() {
113
+ this.cleanupEventListeners();
114
+ }
115
+ cleanupEventListeners() {
116
+ if (this.cleanup) this.cleanup();
117
+ }
104
118
  };
105
119
  export { DropdownMenuSubController };