playbook_ui 16.1.0.pre.alpha.play264213818 → 16.1.0.pre.alpha.play276813969
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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_card/docs/_card_light.html.erb +3 -35
- data/app/pb_kits/playbook/pb_dialog/_dialog.scss +8 -6
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +6 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +83 -13
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_rails.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_react.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.html.erb +52 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.jsx +72 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.html.erb +20 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.md +8 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_react.md +8 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.html.erb +6 -3
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.jsx +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.md +3 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.html.erb +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +6 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +4 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +11 -5
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +15 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +94 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +5 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +7 -2
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +4 -0
- data/app/pb_kits/playbook/pb_dropdown/index.js +184 -77
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +3 -0
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +18 -1
- data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +6 -0
- data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +1 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.html.erb +2 -2
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +16 -9
- data/app/pb_kits/playbook/pb_filter/filter.rb +2 -2
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +1 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +5 -5
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +4 -4
- data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +4 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +638 -549
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +3 -3
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +4 -7
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.md +3 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +4 -4
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +20 -5
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.html.erb +7 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.jsx +24 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +30 -1
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.html.erb +5 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.jsx +14 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +34 -3
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +24 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +2 -1
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +4 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.html.erb +1 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.jsx +1 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
- data/dist/chunks/_typeahead-C4YsbA48.js +1 -0
- data/dist/chunks/vendor.js +2 -2
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/forms/builder/phone_number_field.rb +9 -0
- data/lib/playbook/truncate.rb +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +22 -3
- data/dist/chunks/_typeahead-B9a6ZsEP.js +0 -1
|
@@ -14,6 +14,7 @@ const DROPDOWN_INPUT = "#dropdown-selected-option";
|
|
|
14
14
|
const SEARCH_INPUT_SELECTOR = "[data-dropdown-autocomplete]";
|
|
15
15
|
const SEARCH_BAR_SELECTOR = "[data-dropdown-search]";
|
|
16
16
|
const CLEAR_ICON_SELECTOR = "#dropdown_clear_icon";
|
|
17
|
+
const LABEL_SELECTOR = '[data-dropdown="pb-dropdown-label"]';
|
|
17
18
|
|
|
18
19
|
export default class PbDropdown extends PbEnhancedElement {
|
|
19
20
|
static get selector() {
|
|
@@ -30,14 +31,15 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
30
31
|
connect() {
|
|
31
32
|
// Store instance on element for DatePicker sync
|
|
32
33
|
this.element._pbDropdownInstance = this;
|
|
33
|
-
|
|
34
|
+
|
|
34
35
|
this.keyboardHandler = new PbDropdownKeyboard(this);
|
|
35
36
|
this.isMultiSelect = this.element.dataset.pbDropdownMultiSelect === "true";
|
|
36
37
|
this.formPillProps = this.element.dataset.formPillProps
|
|
37
38
|
? JSON.parse(this.element.dataset.formPillProps)
|
|
38
39
|
: {};
|
|
39
40
|
const baseInput = this.element.querySelector(DROPDOWN_INPUT);
|
|
40
|
-
this.wasOriginallyRequired =
|
|
41
|
+
this.wasOriginallyRequired =
|
|
42
|
+
baseInput && baseInput.hasAttribute("required");
|
|
41
43
|
this.setDefaultValue();
|
|
42
44
|
this.bindEventListeners();
|
|
43
45
|
this.bindSearchInput();
|
|
@@ -48,6 +50,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
48
50
|
this.updatePills();
|
|
49
51
|
|
|
50
52
|
this.clearBtn = this.element.querySelector(CLEAR_ICON_SELECTOR);
|
|
53
|
+
this.isClearable = this.element.dataset.pbDropdownClearable !== "false";
|
|
51
54
|
if (this.clearBtn) {
|
|
52
55
|
this.clearBtn.style.display = "none";
|
|
53
56
|
this.clearBtn.addEventListener("click", (e) => {
|
|
@@ -60,6 +63,10 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
60
63
|
|
|
61
64
|
updateClearButton() {
|
|
62
65
|
if (!this.clearBtn) return;
|
|
66
|
+
if (!this.isClearable) {
|
|
67
|
+
this.clearBtn.style.display = "none";
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
63
70
|
const hasSelection = this.isMultiSelect
|
|
64
71
|
? this.selectedOptions.size > 0
|
|
65
72
|
: Boolean(this.element.querySelector(DROPDOWN_INPUT).value);
|
|
@@ -70,15 +77,24 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
70
77
|
bindEventListeners() {
|
|
71
78
|
const customTrigger =
|
|
72
79
|
this.element.querySelector(CUSTOM_DISPLAY_SELECTOR) || this.element;
|
|
73
|
-
customTrigger.addEventListener("click", () =>
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
customTrigger.addEventListener("click", (e) => {
|
|
81
|
+
const label = e.target.closest(LABEL_SELECTOR);
|
|
82
|
+
if (label && label.htmlFor) {
|
|
83
|
+
const trigger = this.element.querySelector(
|
|
84
|
+
`#${CSS.escape(label.htmlFor)}`,
|
|
85
|
+
);
|
|
86
|
+
if (trigger) {
|
|
87
|
+
trigger.focus();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
this.toggleElement(this.target);
|
|
91
|
+
});
|
|
76
92
|
|
|
77
93
|
this.target.addEventListener("click", this.handleOptionClick.bind(this));
|
|
78
94
|
document.addEventListener(
|
|
79
95
|
"click",
|
|
80
96
|
this.handleDocumentClick.bind(this),
|
|
81
|
-
true
|
|
97
|
+
true,
|
|
82
98
|
);
|
|
83
99
|
}
|
|
84
100
|
|
|
@@ -87,7 +103,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
87
103
|
if (!this.searchBar) return;
|
|
88
104
|
|
|
89
105
|
this.searchBar.addEventListener("input", (e) =>
|
|
90
|
-
this.handleSearch(e.target.value)
|
|
106
|
+
this.handleSearch(e.target.value),
|
|
91
107
|
);
|
|
92
108
|
}
|
|
93
109
|
|
|
@@ -102,46 +118,85 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
102
118
|
|
|
103
119
|
// Live filter
|
|
104
120
|
this.searchInput.addEventListener("input", (e) =>
|
|
105
|
-
this.handleSearch(e.target.value)
|
|
121
|
+
this.handleSearch(e.target.value),
|
|
106
122
|
);
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
adjustDropdownHeight() {
|
|
110
126
|
if (this.target.classList.contains("open")) {
|
|
111
127
|
const el = this.target;
|
|
128
|
+
const shouldConstrain = el.classList.contains("constrain_height");
|
|
112
129
|
el.style.height = "auto";
|
|
113
130
|
requestAnimationFrame(() => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
131
|
+
if (shouldConstrain) {
|
|
132
|
+
// Calculate 18em in pixels (matches SCSS max-height: 18em)
|
|
133
|
+
const fontSize = parseFloat(getComputedStyle(el).fontSize) || 16;
|
|
134
|
+
const maxHeight = fontSize * 18;
|
|
135
|
+
const scrollHeight = el.scrollHeight;
|
|
136
|
+
const newHeight = Math.min(scrollHeight, maxHeight);
|
|
137
|
+
el.offsetHeight; // force reflow
|
|
138
|
+
el.style.height = newHeight + "px";
|
|
139
|
+
} else {
|
|
140
|
+
el.offsetHeight; // force reflow
|
|
141
|
+
el.style.height = el.scrollHeight + "px";
|
|
142
|
+
}
|
|
117
143
|
});
|
|
118
144
|
}
|
|
119
145
|
}
|
|
120
146
|
|
|
147
|
+
adjustDropdownPosition(container) {
|
|
148
|
+
if (!container) return;
|
|
149
|
+
|
|
150
|
+
const wrapper = this.element.querySelector(".dropdown_wrapper");
|
|
151
|
+
if (!wrapper) return;
|
|
152
|
+
|
|
153
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
154
|
+
const h = container.getBoundingClientRect().height || container.scrollHeight;
|
|
155
|
+
const spaceBelow = window.innerHeight - wrapperRect.bottom;
|
|
156
|
+
const spaceAbove = wrapperRect.top;
|
|
157
|
+
|
|
158
|
+
// If not enough space below but enough space above, position above
|
|
159
|
+
if (spaceBelow < h + 10 && spaceAbove >= h + 10) {
|
|
160
|
+
container.style.top = "auto";
|
|
161
|
+
container.style.bottom = "calc(100% + 5px)";
|
|
162
|
+
container.style.marginTop = "0";
|
|
163
|
+
container.style.marginBottom = "0";
|
|
164
|
+
} else {
|
|
165
|
+
// Default: position below
|
|
166
|
+
container.style.top = "";
|
|
167
|
+
container.style.bottom = "";
|
|
168
|
+
container.style.marginTop = "";
|
|
169
|
+
container.style.marginBottom = "";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
121
173
|
handleSearch(term = "") {
|
|
122
174
|
const lcTerm = term.toLowerCase();
|
|
123
|
-
let hasMatch = false
|
|
175
|
+
let hasMatch = false;
|
|
124
176
|
this.element.querySelectorAll(OPTION_SELECTOR).forEach((opt) => {
|
|
125
177
|
//make it so that if the option is selected, it will not show up in the search results
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
178
|
+
if (
|
|
179
|
+
this.isMultiSelect &&
|
|
180
|
+
this.selectedOptions.has(opt.dataset.dropdownOptionLabel)
|
|
181
|
+
) {
|
|
182
|
+
opt.style.display = "none";
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
130
185
|
const label = JSON.parse(opt.dataset.dropdownOptionLabel)
|
|
131
186
|
.label.toString()
|
|
132
187
|
.toLowerCase();
|
|
133
188
|
|
|
134
|
-
|
|
189
|
+
// hide or show option
|
|
135
190
|
const match = label.includes(lcTerm);
|
|
136
191
|
opt.style.display = match ? "" : "none";
|
|
137
|
-
if (match) hasMatch = true
|
|
192
|
+
if (match) hasMatch = true;
|
|
138
193
|
});
|
|
139
194
|
|
|
140
195
|
this.adjustDropdownHeight();
|
|
141
196
|
|
|
142
|
-
this.removeNoOptionsMessage()
|
|
197
|
+
this.removeNoOptionsMessage();
|
|
143
198
|
if (!hasMatch) {
|
|
144
|
-
this.showNoOptionsMessage()
|
|
199
|
+
this.showNoOptionsMessage();
|
|
145
200
|
}
|
|
146
201
|
}
|
|
147
202
|
|
|
@@ -149,7 +204,8 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
149
204
|
if (this.element.querySelector(".dropdown_no_options")) return;
|
|
150
205
|
|
|
151
206
|
const noOptionElement = document.createElement("div");
|
|
152
|
-
noOptionElement.className =
|
|
207
|
+
noOptionElement.className =
|
|
208
|
+
"pb_body_kit_light dropdown_no_options pb_item_kit p_xs display_flex justify_content_center";
|
|
153
209
|
noOptionElement.textContent = "no option";
|
|
154
210
|
|
|
155
211
|
this.target.appendChild(noOptionElement);
|
|
@@ -200,6 +256,8 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
200
256
|
}
|
|
201
257
|
|
|
202
258
|
isClickOutside(event) {
|
|
259
|
+
const label = event.target.closest(LABEL_SELECTOR);
|
|
260
|
+
if (label && this.element.contains(label)) return false;
|
|
203
261
|
const customTrigger = this.element.querySelector(CUSTOM_DISPLAY_SELECTOR);
|
|
204
262
|
if (customTrigger) {
|
|
205
263
|
return !customTrigger.contains(event.target);
|
|
@@ -230,8 +288,8 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
230
288
|
? JSON.parse(
|
|
231
289
|
this.element.querySelector(
|
|
232
290
|
OPTION_SELECTOR +
|
|
233
|
-
`[data-dropdown-option-label*='"id":"${hiddenInput.value}"']
|
|
234
|
-
).dataset.dropdownOptionLabel
|
|
291
|
+
`[data-dropdown-option-label*='"id":"${hiddenInput.value}"']`,
|
|
292
|
+
).dataset.dropdownOptionLabel,
|
|
235
293
|
)
|
|
236
294
|
: null;
|
|
237
295
|
}
|
|
@@ -240,14 +298,14 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
240
298
|
new CustomEvent("pb:dropdown:selected", {
|
|
241
299
|
detail,
|
|
242
300
|
bubbles: true,
|
|
243
|
-
})
|
|
301
|
+
}),
|
|
244
302
|
);
|
|
245
303
|
}
|
|
246
304
|
|
|
247
305
|
onOptionSelected(value, selectedOption) {
|
|
248
306
|
const triggerElement = this.element.querySelector(DROPDOWN_TRIGGER_DISPLAY);
|
|
249
307
|
const customDisplayElement = this.element.querySelector(
|
|
250
|
-
"#dropdown_trigger_custom_display"
|
|
308
|
+
"#dropdown_trigger_custom_display",
|
|
251
309
|
);
|
|
252
310
|
|
|
253
311
|
if (triggerElement) {
|
|
@@ -255,36 +313,46 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
255
313
|
const selectedLabel = JSON.parse(value).label;
|
|
256
314
|
triggerElement.textContent = selectedLabel;
|
|
257
315
|
this.emitSelectionChange();
|
|
258
|
-
|
|
316
|
+
|
|
259
317
|
// Handle quickpick variant: populate start/end date hidden inputs
|
|
260
318
|
const optionData = JSON.parse(value);
|
|
261
319
|
const startDateId = this.element.dataset.startDateId;
|
|
262
320
|
const endDateId = this.element.dataset.endDateId;
|
|
263
321
|
const controlsStartId = this.element.dataset.controlsStartId;
|
|
264
322
|
const controlsEndId = this.element.dataset.controlsEndId;
|
|
265
|
-
|
|
323
|
+
|
|
266
324
|
if (optionData.formatted_start_date && optionData.formatted_end_date) {
|
|
267
325
|
// Populate date inputs when option has date fields
|
|
268
326
|
if (startDateId) {
|
|
269
327
|
const startDateInput = document.getElementById(startDateId);
|
|
270
|
-
if (startDateInput)
|
|
328
|
+
if (startDateInput)
|
|
329
|
+
startDateInput.value = optionData.formatted_start_date;
|
|
271
330
|
}
|
|
272
|
-
|
|
331
|
+
|
|
273
332
|
if (endDateId) {
|
|
274
333
|
const endDateInput = document.getElementById(endDateId);
|
|
275
|
-
if (endDateInput)
|
|
334
|
+
if (endDateInput)
|
|
335
|
+
endDateInput.value = optionData.formatted_end_date;
|
|
276
336
|
}
|
|
277
|
-
|
|
337
|
+
|
|
278
338
|
// Sync with DatePickers if controlsStartId/controlsEndId are present
|
|
279
339
|
if (controlsStartId) {
|
|
280
|
-
const startPicker = document.querySelector(
|
|
340
|
+
const startPicker = document.querySelector(
|
|
341
|
+
`#${controlsStartId}`,
|
|
342
|
+
)?._flatpickr;
|
|
281
343
|
if (startPicker) {
|
|
282
|
-
startPicker.setDate(
|
|
344
|
+
startPicker.setDate(
|
|
345
|
+
optionData.formatted_start_date,
|
|
346
|
+
true,
|
|
347
|
+
"m/d/Y",
|
|
348
|
+
);
|
|
283
349
|
}
|
|
284
350
|
}
|
|
285
|
-
|
|
351
|
+
|
|
286
352
|
if (controlsEndId) {
|
|
287
|
-
const endPicker = document.querySelector(
|
|
353
|
+
const endPicker = document.querySelector(
|
|
354
|
+
`#${controlsEndId}`,
|
|
355
|
+
)?._flatpickr;
|
|
288
356
|
if (endPicker) {
|
|
289
357
|
endPicker.setDate(optionData.formatted_end_date, true, "m/d/Y");
|
|
290
358
|
}
|
|
@@ -295,22 +363,26 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
295
363
|
const startDateInput = document.getElementById(startDateId);
|
|
296
364
|
if (startDateInput) startDateInput.value = "";
|
|
297
365
|
}
|
|
298
|
-
|
|
366
|
+
|
|
299
367
|
if (endDateId) {
|
|
300
368
|
const endDateInput = document.getElementById(endDateId);
|
|
301
369
|
if (endDateInput) endDateInput.value = "";
|
|
302
370
|
}
|
|
303
|
-
|
|
371
|
+
|
|
304
372
|
// Clear DatePickers as well
|
|
305
373
|
if (controlsStartId) {
|
|
306
|
-
const startPicker = document.querySelector(
|
|
374
|
+
const startPicker = document.querySelector(
|
|
375
|
+
`#${controlsStartId}`,
|
|
376
|
+
)?._flatpickr;
|
|
307
377
|
if (startPicker) {
|
|
308
378
|
startPicker.clear();
|
|
309
379
|
}
|
|
310
380
|
}
|
|
311
|
-
|
|
381
|
+
|
|
312
382
|
if (controlsEndId) {
|
|
313
|
-
const endPicker = document.querySelector(
|
|
383
|
+
const endPicker = document.querySelector(
|
|
384
|
+
`#${controlsEndId}`,
|
|
385
|
+
)?._flatpickr;
|
|
314
386
|
if (endPicker) {
|
|
315
387
|
endPicker.clear();
|
|
316
388
|
}
|
|
@@ -350,7 +422,9 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
350
422
|
this.adjustDropdownHeight();
|
|
351
423
|
}
|
|
352
424
|
});
|
|
353
|
-
this.element.querySelector(DROPDOWN_INPUT).value = Array.from(
|
|
425
|
+
this.element.querySelector(DROPDOWN_INPUT).value = Array.from(
|
|
426
|
+
this.selectedOptions,
|
|
427
|
+
)
|
|
354
428
|
.map((opt) => JSON.parse(opt).id)
|
|
355
429
|
.join(",");
|
|
356
430
|
} else {
|
|
@@ -365,7 +439,21 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
365
439
|
showElement(elem) {
|
|
366
440
|
elem.classList.remove("close");
|
|
367
441
|
elem.classList.add("open");
|
|
368
|
-
|
|
442
|
+
|
|
443
|
+
const shouldConstrain = elem.classList.contains("constrain_height");
|
|
444
|
+
if (shouldConstrain) {
|
|
445
|
+
// Calculate height respecting max-height constraint (18em)
|
|
446
|
+
const fontSize = parseFloat(getComputedStyle(elem).fontSize) || 16;
|
|
447
|
+
const maxHeight = fontSize * 18; // matches SCSS max-height: 18em
|
|
448
|
+
const scrollHeight = elem.scrollHeight;
|
|
449
|
+
const height = Math.min(scrollHeight, maxHeight);
|
|
450
|
+
elem.style.height = height + "px";
|
|
451
|
+
} else {
|
|
452
|
+
elem.style.height = elem.scrollHeight + "px";
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Auto-position dropdown above if not enough space below
|
|
456
|
+
this.adjustDropdownPosition(elem);
|
|
369
457
|
}
|
|
370
458
|
|
|
371
459
|
hideElement(elem) {
|
|
@@ -382,7 +470,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
382
470
|
this.keyboardHandler.focusedOptionIndex = -1;
|
|
383
471
|
const options = this.element.querySelectorAll(OPTION_SELECTOR);
|
|
384
472
|
options.forEach((option) =>
|
|
385
|
-
option.classList.remove("pb_dropdown_option_focused")
|
|
473
|
+
option.classList.remove("pb_dropdown_option_focused"),
|
|
386
474
|
);
|
|
387
475
|
}
|
|
388
476
|
}
|
|
@@ -417,7 +505,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
417
505
|
hiddenInput.closest(".dropdown_wrapper").classList.add("error");
|
|
418
506
|
}
|
|
419
507
|
},
|
|
420
|
-
true
|
|
508
|
+
true,
|
|
421
509
|
);
|
|
422
510
|
}
|
|
423
511
|
|
|
@@ -427,7 +515,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
427
515
|
const dropdownWrapperElement = input.closest(".dropdown_wrapper");
|
|
428
516
|
dropdownWrapperElement.classList.remove("error");
|
|
429
517
|
const errorLabelElement = dropdownWrapperElement.querySelector(
|
|
430
|
-
".pb_body_kit_negative"
|
|
518
|
+
".pb_body_kit_negative",
|
|
431
519
|
);
|
|
432
520
|
if (errorLabelElement) {
|
|
433
521
|
errorLabelElement.remove();
|
|
@@ -435,13 +523,13 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
435
523
|
return;
|
|
436
524
|
}
|
|
437
525
|
}
|
|
438
|
-
|
|
526
|
+
|
|
439
527
|
if (input.checkValidity()) {
|
|
440
528
|
const dropdownWrapperElement = input.closest(".dropdown_wrapper");
|
|
441
529
|
dropdownWrapperElement.classList.remove("error");
|
|
442
530
|
|
|
443
531
|
const errorLabelElement = dropdownWrapperElement.querySelector(
|
|
444
|
-
".pb_body_kit_negative"
|
|
532
|
+
".pb_body_kit_negative",
|
|
445
533
|
);
|
|
446
534
|
if (errorLabelElement) {
|
|
447
535
|
errorLabelElement.remove();
|
|
@@ -452,7 +540,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
452
540
|
setDefaultValue() {
|
|
453
541
|
const hiddenInput = this.element.querySelector(DROPDOWN_INPUT);
|
|
454
542
|
const optionEls = Array.from(
|
|
455
|
-
this.element.querySelectorAll(OPTION_SELECTOR)
|
|
543
|
+
this.element.querySelectorAll(OPTION_SELECTOR),
|
|
456
544
|
);
|
|
457
545
|
const defaultValue = hiddenInput.dataset.defaultValue || "";
|
|
458
546
|
if (!defaultValue) return;
|
|
@@ -498,44 +586,53 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
498
586
|
selectedOption.classList.add("pb_dropdown_option_selected");
|
|
499
587
|
const optionData = JSON.parse(selectedOption.dataset.dropdownOptionLabel);
|
|
500
588
|
this.setTriggerElementText(optionData.label);
|
|
501
|
-
|
|
589
|
+
|
|
502
590
|
// Handle quickpick variant: populate start/end date hidden inputs and sync DatePickers
|
|
503
591
|
if (optionData.formatted_start_date && optionData.formatted_end_date) {
|
|
504
592
|
const startDateId = this.element.dataset.startDateId;
|
|
505
593
|
const endDateId = this.element.dataset.endDateId;
|
|
506
594
|
const controlsStartId = this.element.dataset.controlsStartId;
|
|
507
595
|
const controlsEndId = this.element.dataset.controlsEndId;
|
|
508
|
-
|
|
596
|
+
|
|
509
597
|
if (startDateId) {
|
|
510
598
|
const startDateInput = document.getElementById(startDateId);
|
|
511
|
-
if (startDateInput)
|
|
599
|
+
if (startDateInput)
|
|
600
|
+
startDateInput.value = optionData.formatted_start_date;
|
|
512
601
|
}
|
|
513
|
-
|
|
602
|
+
|
|
514
603
|
if (endDateId) {
|
|
515
604
|
const endDateInput = document.getElementById(endDateId);
|
|
516
605
|
if (endDateInput) endDateInput.value = optionData.formatted_end_date;
|
|
517
606
|
}
|
|
518
|
-
|
|
607
|
+
|
|
519
608
|
// Sync with DatePickers - retry with delays to ensure DatePickers are initialized
|
|
520
609
|
const syncDatePickers = () => {
|
|
521
610
|
if (controlsStartId) {
|
|
522
|
-
const startPicker = document.querySelector(
|
|
611
|
+
const startPicker = document.querySelector(
|
|
612
|
+
`#${controlsStartId}`,
|
|
613
|
+
)?._flatpickr;
|
|
523
614
|
if (startPicker) {
|
|
524
|
-
startPicker.setDate(
|
|
615
|
+
startPicker.setDate(
|
|
616
|
+
optionData.formatted_start_date,
|
|
617
|
+
true,
|
|
618
|
+
"m/d/Y",
|
|
619
|
+
);
|
|
525
620
|
}
|
|
526
621
|
}
|
|
527
|
-
|
|
622
|
+
|
|
528
623
|
if (controlsEndId) {
|
|
529
|
-
const endPicker = document.querySelector(
|
|
624
|
+
const endPicker = document.querySelector(
|
|
625
|
+
`#${controlsEndId}`,
|
|
626
|
+
)?._flatpickr;
|
|
530
627
|
if (endPicker) {
|
|
531
628
|
endPicker.setDate(optionData.formatted_end_date, true, "m/d/Y");
|
|
532
629
|
}
|
|
533
630
|
}
|
|
534
631
|
};
|
|
535
|
-
|
|
632
|
+
|
|
536
633
|
// Try immediately
|
|
537
634
|
syncDatePickers();
|
|
538
|
-
|
|
635
|
+
|
|
539
636
|
// Retry after short delay in case DatePickers aren't ready yet
|
|
540
637
|
setTimeout(syncDatePickers, 100);
|
|
541
638
|
setTimeout(syncDatePickers, 300);
|
|
@@ -598,7 +695,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
598
695
|
|
|
599
696
|
const wrapper = this.element.querySelector("#dropdown_pills_wrapper");
|
|
600
697
|
const placeholder = this.element.querySelector(
|
|
601
|
-
"#dropdown_trigger_display_multi_select"
|
|
698
|
+
"#dropdown_trigger_display_multi_select",
|
|
602
699
|
);
|
|
603
700
|
if (!wrapper) return;
|
|
604
701
|
|
|
@@ -616,7 +713,12 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
616
713
|
// Create a form pill for each selected option
|
|
617
714
|
const pill = document.createElement("div");
|
|
618
715
|
const color = this.formPillProps.color || "primary";
|
|
619
|
-
pill.classList.add(
|
|
716
|
+
pill.classList.add(
|
|
717
|
+
"pb_form_pill_kit",
|
|
718
|
+
`pb_form_pill_${color}`,
|
|
719
|
+
"pb_form_pill_none",
|
|
720
|
+
"mr_xs",
|
|
721
|
+
);
|
|
620
722
|
if (this.formPillProps.size === "small") {
|
|
621
723
|
pill.classList.add("pb_form_pill_small");
|
|
622
724
|
}
|
|
@@ -641,8 +743,8 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
641
743
|
|
|
642
744
|
const optEl = this.element.querySelector(
|
|
643
745
|
`${OPTION_SELECTOR}[data-dropdown-option-label*='"id":${JSON.stringify(
|
|
644
|
-
id
|
|
645
|
-
)}']
|
|
746
|
+
id,
|
|
747
|
+
)}']`,
|
|
646
748
|
);
|
|
647
749
|
if (optEl) {
|
|
648
750
|
optEl.style.display = "";
|
|
@@ -671,18 +773,18 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
671
773
|
}
|
|
672
774
|
}
|
|
673
775
|
const customDisplay = this.element.querySelector(
|
|
674
|
-
"#dropdown_trigger_custom_display"
|
|
776
|
+
"#dropdown_trigger_custom_display",
|
|
675
777
|
);
|
|
676
778
|
if (customDisplay) {
|
|
677
779
|
customDisplay.style.display = "none";
|
|
678
780
|
}
|
|
679
|
-
|
|
781
|
+
|
|
680
782
|
// Clear quickpick hidden inputs
|
|
681
783
|
const startDateId = this.element.dataset.startDateId;
|
|
682
784
|
const endDateId = this.element.dataset.endDateId;
|
|
683
785
|
const controlsStartId = this.element.dataset.controlsStartId;
|
|
684
786
|
const controlsEndId = this.element.dataset.controlsEndId;
|
|
685
|
-
|
|
787
|
+
|
|
686
788
|
if (startDateId) {
|
|
687
789
|
const startDateInput = document.getElementById(startDateId);
|
|
688
790
|
if (startDateInput) startDateInput.value = "";
|
|
@@ -691,22 +793,24 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
691
793
|
const endDateInput = document.getElementById(endDateId);
|
|
692
794
|
if (endDateInput) endDateInput.value = "";
|
|
693
795
|
}
|
|
694
|
-
|
|
796
|
+
|
|
695
797
|
// Clear linked DatePickers if controlsStartId/controlsEndId are present
|
|
696
798
|
if (controlsStartId) {
|
|
697
|
-
const startPicker = document.querySelector(
|
|
799
|
+
const startPicker = document.querySelector(
|
|
800
|
+
`#${controlsStartId}`,
|
|
801
|
+
)?._flatpickr;
|
|
698
802
|
if (startPicker) {
|
|
699
803
|
startPicker.clear();
|
|
700
804
|
}
|
|
701
805
|
}
|
|
702
|
-
|
|
806
|
+
|
|
703
807
|
if (controlsEndId) {
|
|
704
808
|
const endPicker = document.querySelector(`#${controlsEndId}`)?._flatpickr;
|
|
705
809
|
if (endPicker) {
|
|
706
810
|
endPicker.clear();
|
|
707
811
|
}
|
|
708
812
|
}
|
|
709
|
-
|
|
813
|
+
|
|
710
814
|
this.resetDropdownValue();
|
|
711
815
|
this.updatePills();
|
|
712
816
|
this.updateClearButton();
|
|
@@ -717,21 +821,24 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
717
821
|
// Method for DatePicker sync - only clears the dropdown, not the DatePickers
|
|
718
822
|
clearSelected() {
|
|
719
823
|
// Only clear if this is a single-select quickpick variant
|
|
720
|
-
if (
|
|
824
|
+
if (
|
|
825
|
+
this.element.dataset.pbDropdownVariant !== "quickpick" ||
|
|
826
|
+
this.isMultiSelect
|
|
827
|
+
) {
|
|
721
828
|
return;
|
|
722
829
|
}
|
|
723
|
-
|
|
830
|
+
|
|
724
831
|
const customDisplay = this.element.querySelector(
|
|
725
|
-
"#dropdown_trigger_custom_display"
|
|
832
|
+
"#dropdown_trigger_custom_display",
|
|
726
833
|
);
|
|
727
834
|
if (customDisplay) {
|
|
728
835
|
customDisplay.style.display = "none";
|
|
729
836
|
}
|
|
730
|
-
|
|
837
|
+
|
|
731
838
|
// Clear quickpick hidden inputs only (not the DatePickers)
|
|
732
839
|
const startDateId = this.element.dataset.startDateId;
|
|
733
840
|
const endDateId = this.element.dataset.endDateId;
|
|
734
|
-
|
|
841
|
+
|
|
735
842
|
if (startDateId) {
|
|
736
843
|
const startDateInput = document.getElementById(startDateId);
|
|
737
844
|
if (startDateInput) startDateInput.value = "";
|
|
@@ -740,7 +847,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
740
847
|
const endDateInput = document.getElementById(endDateId);
|
|
741
848
|
if (endDateInput) endDateInput.value = "";
|
|
742
849
|
}
|
|
743
|
-
|
|
850
|
+
|
|
744
851
|
this.resetDropdownValue();
|
|
745
852
|
this.updateClearButton();
|
|
746
853
|
this.emitSelectionChange();
|
|
@@ -767,7 +874,7 @@ export default class PbDropdown extends PbEnhancedElement {
|
|
|
767
874
|
inp.dataset.generated = "true";
|
|
768
875
|
baseInput.insertAdjacentElement("afterend", inp);
|
|
769
876
|
});
|
|
770
|
-
|
|
877
|
+
|
|
771
878
|
// For multi-select, remove required from base input when there are selections
|
|
772
879
|
// The generated inputs handle the form submission with actual values
|
|
773
880
|
// Restore required attribute when there are no selections (if it was originally required)
|
|
@@ -19,6 +19,7 @@ type DropdownContainerProps = {
|
|
|
19
19
|
aria?: { [key: string]: string };
|
|
20
20
|
children?: React.ReactChild[] | React.ReactChild;
|
|
21
21
|
className?: string;
|
|
22
|
+
constrainHeight?: boolean;
|
|
22
23
|
dark?: boolean;
|
|
23
24
|
data?: { [key: string]: string };
|
|
24
25
|
htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
|
|
@@ -31,6 +32,7 @@ const DropdownContainer = (props: DropdownContainerProps) => {
|
|
|
31
32
|
aria = {},
|
|
32
33
|
children,
|
|
33
34
|
className,
|
|
35
|
+
constrainHeight = false,
|
|
34
36
|
dark = false,
|
|
35
37
|
data = {},
|
|
36
38
|
htmlOptions = {},
|
|
@@ -54,6 +56,7 @@ const DropdownContainer = (props: DropdownContainerProps) => {
|
|
|
54
56
|
const classes = classnames(
|
|
55
57
|
buildCss("pb_dropdown_container"),
|
|
56
58
|
`${isDropDownClosed ? "close" : "open"}`,
|
|
59
|
+
constrainHeight && "constrain_height",
|
|
57
60
|
globalProps(props),
|
|
58
61
|
className
|
|
59
62
|
);
|
|
@@ -44,6 +44,9 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
44
44
|
|
|
45
45
|
const {
|
|
46
46
|
autocomplete,
|
|
47
|
+
clearable,
|
|
48
|
+
error,
|
|
49
|
+
errorId,
|
|
47
50
|
filterItem,
|
|
48
51
|
handleBackspace,
|
|
49
52
|
handleChange,
|
|
@@ -52,8 +55,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
52
55
|
inputWrapperRef,
|
|
53
56
|
isDropDownClosed,
|
|
54
57
|
isInputFocused,
|
|
58
|
+
label: contextLabel,
|
|
55
59
|
multiSelect,
|
|
56
60
|
selected,
|
|
61
|
+
selectId,
|
|
57
62
|
setIsInputFocused,
|
|
58
63
|
toggleDropdown,
|
|
59
64
|
} = useContext(DropdownContext);
|
|
@@ -103,6 +108,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
103
108
|
? placeholder
|
|
104
109
|
: "Select...";
|
|
105
110
|
|
|
111
|
+
const triggerAriaLabel = contextLabel
|
|
112
|
+
? (children ? contextLabel : `${contextLabel}, ${defaultDisplayPlaceholder}`)
|
|
113
|
+
: undefined;
|
|
114
|
+
|
|
106
115
|
return (
|
|
107
116
|
<div {...ariaProps}
|
|
108
117
|
{...dataProps}
|
|
@@ -113,6 +122,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
113
122
|
{
|
|
114
123
|
children ? (
|
|
115
124
|
<div
|
|
125
|
+
aria-describedby={errorId}
|
|
126
|
+
aria-invalid={!!error}
|
|
127
|
+
aria-label={triggerAriaLabel}
|
|
128
|
+
id={selectId}
|
|
116
129
|
onClick={() => toggleDropdown()}
|
|
117
130
|
onKeyDown= {handleKeyDown}
|
|
118
131
|
ref={inputWrapperRef}
|
|
@@ -129,6 +142,10 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
129
142
|
className={triggerWrapperClasses}
|
|
130
143
|
cursor={`${autocomplete ? "text" : "pointer"}`}
|
|
131
144
|
htmlOptions={{
|
|
145
|
+
"aria-describedby": errorId,
|
|
146
|
+
"aria-invalid": !!error,
|
|
147
|
+
"aria-label": triggerAriaLabel,
|
|
148
|
+
id: selectId,
|
|
132
149
|
onClick: () => handleWrapperClick(),
|
|
133
150
|
onKeyDown: handleKeyDown,
|
|
134
151
|
tabIndex: "0",
|
|
@@ -225,7 +242,7 @@ const DropdownTrigger = (props: DropdownTriggerProps) => {
|
|
|
225
242
|
key={`${isDropDownClosed ? "chevron-down" : "chevron-up"}`}
|
|
226
243
|
>
|
|
227
244
|
{
|
|
228
|
-
selectedArray.length > 0 && (
|
|
245
|
+
clearable !== false && selectedArray.length > 0 && (
|
|
229
246
|
<div onClick={(e)=>{e.stopPropagation();handleBackspace()}}>
|
|
230
247
|
<Icon
|
|
231
248
|
cursor="pointer"
|