playbook_ui 15.1.0.pre.alpha.PLAY2468phonenuminputvalidation10803 → 15.1.0.pre.alpha.PLAY251810942
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_button/docs/_button_managed_disabled.html.erb +31 -0
- data/app/pb_kits/playbook/pb_button/docs/_button_managed_disabled.md +7 -0
- data/app/pb_kits/playbook/pb_button/docs/_button_managed_disabled_helper.html.erb +21 -0
- data/app/pb_kits/playbook/pb_button/docs/_button_managed_disabled_helper.md +7 -0
- data/app/pb_kits/playbook/pb_button/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_button/index.js +99 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +105 -29
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -1
- data/app/pb_kits/playbook/pb_form/pb_form_validation.js +8 -40
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +2 -51
- data/dist/chunks/{_line_graph-Bkn-wx30.js → _line_graph-C9stNsP3.js} +1 -1
- data/dist/chunks/{_typeahead-DkRYiut7.js → _typeahead-D3MtsWXG.js} +1 -1
- data/dist/chunks/{_weekday_stacked-BUjWQnqn.js → _weekday_stacked-Bvc7R5vH.js} +1 -1
- data/dist/chunks/pb_form_validation-CleM960_.js +1 -0
- data/dist/chunks/vendor.js +1 -1
- data/dist/menu.yml +1 -1
- data/dist/playbook-doc.js +1 -1
- 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/version.rb +1 -1
- metadata +11 -6
- data/dist/chunks/pb_form_validation-B4uRmBwC.js +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ea9f5c8acfb10e8fb3d40fea187b82854b1fb068361853c9b032f91fca4685ad
|
|
4
|
+
data.tar.gz: ff8472f9f4055f5b24cb17583c02c37fa5cc022f37bf7d36869cb4d2dc06feaf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a7a3df82c8d6bb097dc840c452b3658a4751f3ecb1efde791db7043328cca7a1450adb4d56b0b1d52e13ebd7e75163573105a2910bd01faeca49c05937086f83
|
|
7
|
+
data.tar.gz: 869e0b7510f081c28a277b0f77f1c96e2f63dccfc5955913140a00952741545f79e728370a234ded1cedba4fde080bbd396650e357a37b24ede37ac55428bfff
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<%= pb_rails("body", props: { text: "Click to disable the Buttons below", id: "toggle-disabled-demo", cursor: "pointer", color:"link", margin_bottom:"sm" }) %>
|
|
2
|
+
<%= pb_rails("body", props: { text: "Click to enable the Buttons below", id: "toggle-enabled-demo", cursor: "pointer", color:"link", margin_bottom:"sm" }) %>
|
|
3
|
+
|
|
4
|
+
<%= pb_rails("card", props:{display:"flex", flex_direction:"row", justify_content:"center"}) do %>
|
|
5
|
+
<%= pb_rails("button", props: { text: "I am a Button", id: "normal_managed_button", data:{pb_button_managed: true}, margin_right: "lg" }) %>
|
|
6
|
+
<%= pb_rails("button", props: { text: "I am an <a> Button", id: "a_tag_managed_button", tag:"a", data:{pb_button_managed: true}, link: "http://google.com"}) %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<script>
|
|
9
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
10
|
+
const disableTrigger = document.querySelector('#toggle-disabled-demo')
|
|
11
|
+
const enableTrigger = document.querySelector('#toggle-enabled-demo')
|
|
12
|
+
|
|
13
|
+
// Find the Buttons you want to 'manage'
|
|
14
|
+
const btn = document.querySelector('#normal_managed_button');
|
|
15
|
+
const link = document.querySelector('#a_tag_managed_button');
|
|
16
|
+
|
|
17
|
+
disableTrigger.addEventListener('click', (e) => {
|
|
18
|
+
// Disable default button
|
|
19
|
+
btn.setAttribute('disabled', true)
|
|
20
|
+
// Disable a tag button
|
|
21
|
+
link.setAttribute('aria-disabled', 'true')
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
enableTrigger.addEventListener('click', (e) => {
|
|
25
|
+
// Enable default button
|
|
26
|
+
btn.removeAttribute('disabled')
|
|
27
|
+
// Enable a tag button
|
|
28
|
+
link.removeAttribute('aria-disabled')
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
If needing to toggle the disabled state of the Button dynamically (for example, within a Turbo or Stimulus context), you can now do so in rails using the `pb-button-managed` data attribute.
|
|
2
|
+
|
|
3
|
+
1) Add the following data attribute to your button kit: `data:{ pb-button-managed: true }`
|
|
4
|
+
|
|
5
|
+
2) To toggle enabled/disabled state via attributes: for buttons set/remove disabled, for links set/remove aria-disabled="true". This will handle disabling the button, preventing clicks as well as all style changes so you don't have to.
|
|
6
|
+
|
|
7
|
+
Click to enable or disable the buttons above and view the code snippet below for details!
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%= pb_rails("body", props: { text: "Click to disable the Button below", id: "toggle-disabled-demo-with-helper", cursor: "pointer", color:"link", margin_bottom:"sm" }) %>
|
|
2
|
+
<%= pb_rails("body", props: { text: "Click to enable the Button below", id: "toggle-enabled-demo-with-helper", cursor: "pointer", color:"link", margin_bottom:"sm" }) %>
|
|
3
|
+
<br/>
|
|
4
|
+
<%= pb_rails("card", props:{display:"flex", flex_direction:"row", justify_content:"center"}) do %>
|
|
5
|
+
<%= pb_rails("button", props: { text: "Watch me Change!", id: "managed_button_with_helper", data:{pb_button_managed: true} }) %>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
10
|
+
const disable = document.querySelector('#toggle-disabled-demo-with-helper')
|
|
11
|
+
const enable = document.querySelector('#toggle-enabled-demo-with-helper')
|
|
12
|
+
|
|
13
|
+
// Find the Button you want to 'manage'
|
|
14
|
+
const demoBtn = document.querySelector('#managed_button_with_helper')
|
|
15
|
+
|
|
16
|
+
// Use the pbButton object created by the kit to call the enable/disable methods
|
|
17
|
+
disable.addEventListener('click', (e) => {demoBtn._pbButton.disable()});
|
|
18
|
+
enable.addEventListener('click', (e) => {demoBtn._pbButton.enable()});
|
|
19
|
+
|
|
20
|
+
});
|
|
21
|
+
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
The disabled state for the button can also be toggled via small helpers available through the `pb-button-managed` data attribute.
|
|
2
|
+
|
|
3
|
+
1) Add the following data attribute to your button kit: `data:{ pb-button-managed: true }`
|
|
4
|
+
|
|
5
|
+
2) Toggle state via the provided `_pbButton.disable()` and `_pbButton.enable()` helpers as shown in the code snippet below.
|
|
6
|
+
|
|
7
|
+
Click to enable or disable the buttons above to see this in action!
|
|
@@ -11,6 +11,8 @@ examples:
|
|
|
11
11
|
- button_options: Button Additional Options
|
|
12
12
|
- button_size: Button Size
|
|
13
13
|
- button_form: Button Form Attribute
|
|
14
|
+
- button_managed_disabled: Button Toggle Disabled State
|
|
15
|
+
- button_managed_disabled_helper: Button Toggle Disabled State Helper
|
|
14
16
|
|
|
15
17
|
react:
|
|
16
18
|
- button_default: Button Variants
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import PbEnhancedElement from "../pb_enhanced_element"
|
|
2
|
+
|
|
3
|
+
const BUTTON_SELECTOR = "[data-pb-button-managed]"
|
|
4
|
+
|
|
5
|
+
export default class PbButton extends PbEnhancedElement {
|
|
6
|
+
static get selector() {
|
|
7
|
+
return BUTTON_SELECTOR
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
connect() {
|
|
11
|
+
this._attrManaged = this._attributesPresent()
|
|
12
|
+
this.element._pbButton = this
|
|
13
|
+
|
|
14
|
+
this._onClick = (e) => {
|
|
15
|
+
if (this.isDisabled()) {
|
|
16
|
+
e.preventDefault()
|
|
17
|
+
e.stopImmediatePropagation()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
this.element.addEventListener("click", this._onClick, true)
|
|
21
|
+
|
|
22
|
+
if (this._attrManaged) this._syncClassesFromAttributes()
|
|
23
|
+
|
|
24
|
+
this._observer = new MutationObserver(() => {
|
|
25
|
+
this._attrManaged = true
|
|
26
|
+
this._syncClassesFromAttributes()
|
|
27
|
+
})
|
|
28
|
+
this._observer.observe(this.element, {
|
|
29
|
+
attributes: true,
|
|
30
|
+
attributeFilter: ["disabled", "aria-disabled"],
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
disconnect() {
|
|
35
|
+
this.element.removeEventListener("click", this._onClick, true)
|
|
36
|
+
this._observer?.disconnect()
|
|
37
|
+
delete this.element._pbButton
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
disable() { this.setDisabled(true) }
|
|
41
|
+
enable() { this.setDisabled(false) }
|
|
42
|
+
|
|
43
|
+
setDisabled(state) {
|
|
44
|
+
if (this._isButton()) {
|
|
45
|
+
state
|
|
46
|
+
? this.element.setAttribute("disabled", "disabled")
|
|
47
|
+
: this.element.removeAttribute("disabled")
|
|
48
|
+
} else {
|
|
49
|
+
state
|
|
50
|
+
? this.element.setAttribute("aria-disabled", "true")
|
|
51
|
+
: this.element.removeAttribute("aria-disabled")
|
|
52
|
+
}
|
|
53
|
+
this._attrManaged = true
|
|
54
|
+
this._applyClassState(state)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
isDisabled() {
|
|
58
|
+
if (this._isButton()) {
|
|
59
|
+
if (this.element.hasAttribute("disabled")) return true
|
|
60
|
+
if (this._attrManaged && !this.element.hasAttribute("disabled")) return false
|
|
61
|
+
} else {
|
|
62
|
+
const aria = this.element.getAttribute("aria-disabled")
|
|
63
|
+
if (aria === "true") return true
|
|
64
|
+
if (this._attrManaged && aria !== "true") return false
|
|
65
|
+
}
|
|
66
|
+
return this.element.classList.contains("pb_button_disabled")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_isButton() {
|
|
70
|
+
return this.element.tagName === "BUTTON"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_attributesPresent() {
|
|
74
|
+
return this.element.hasAttribute("disabled") || this.element.hasAttribute("aria-disabled")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_syncClassesFromAttributes() {
|
|
78
|
+
const state = this._attrDisabledState()
|
|
79
|
+
const disabled = (state === null) ? false : state
|
|
80
|
+
this._applyClassState(disabled)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_attrDisabledState() {
|
|
84
|
+
if (this._isButton()) {
|
|
85
|
+
return this.element.hasAttribute("disabled") ? true : null
|
|
86
|
+
} else {
|
|
87
|
+
const aria = this.element.getAttribute("aria-disabled")
|
|
88
|
+
if (aria === "true") return true
|
|
89
|
+
if (aria === "false") return false
|
|
90
|
+
return this.element.hasAttribute("aria-disabled") ? false : null
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
_applyClassState(disabled) {
|
|
95
|
+
this.element.classList.toggle("pb_button_disabled", !!disabled)
|
|
96
|
+
this.element.classList.toggle("pb_button_enabled", !disabled)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
@@ -10,9 +10,15 @@
|
|
|
10
10
|
@import "./scss_partials/dropdown_animation";
|
|
11
11
|
@import "dropdown_mixin";
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
.pb_dropdown_default,
|
|
14
|
+
.pb_dropdown_subtle,
|
|
15
|
+
.pb_dropdown_default_separators_hidden,
|
|
16
|
+
.pb_dropdown_subtle_separators_hidden {
|
|
14
17
|
.dropdown_wrapper {
|
|
15
|
-
|
|
18
|
+
.dropdown_trigger_wrapper,
|
|
19
|
+
.dropdown_trigger_wrapper_focus,
|
|
20
|
+
.dropdown_trigger_wrapper_focus_select_only,
|
|
21
|
+
.dropdown_trigger_wrapper_select_only {
|
|
16
22
|
@include pb_body;
|
|
17
23
|
border: 1px solid $border_light;
|
|
18
24
|
background-color: $white;
|
|
@@ -48,11 +54,11 @@
|
|
|
48
54
|
transition: box-shadow 0.15s ease-in-out;
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
&.dropdown_trigger_wrapper_select_only {
|
|
52
58
|
box-shadow: inset 0 -11px 20px rgba($primary, 0.05);
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
&.dropdown_trigger_wrapper_focus {
|
|
56
62
|
box-shadow: 0px 0px 0 1px $primary !important;
|
|
57
63
|
transition: box-shadow 0.1s ease-in-out;
|
|
58
64
|
}
|
|
@@ -69,7 +75,12 @@
|
|
|
69
75
|
z-index: $z_1;
|
|
70
76
|
width: 100%;
|
|
71
77
|
|
|
72
|
-
|
|
78
|
+
.pb_dropdown_option,
|
|
79
|
+
.pb_dropdown_option_list,
|
|
80
|
+
.pb_dropdown_option_selected,
|
|
81
|
+
.pb_dropdown_option_focused,
|
|
82
|
+
.pb_dropdown_option_list_focused,
|
|
83
|
+
.pb_dropdown_option_selected_focused {
|
|
73
84
|
padding-left: $space_sm;
|
|
74
85
|
padding-right: $space_sm;
|
|
75
86
|
padding-top: $space_xs;
|
|
@@ -79,11 +90,14 @@
|
|
|
79
90
|
background-color: $bg_light;
|
|
80
91
|
}
|
|
81
92
|
|
|
82
|
-
|
|
93
|
+
&.pb_dropdown_option_focused,
|
|
94
|
+
&.pb_dropdown_option_list_focused,
|
|
95
|
+
&.pb_dropdown_option_selected_focused {
|
|
83
96
|
background-color: $bg_light;
|
|
84
97
|
}
|
|
85
98
|
|
|
86
|
-
|
|
99
|
+
&.pb_dropdown_option_list,
|
|
100
|
+
&.pb_dropdown_option_list_focused {
|
|
87
101
|
border-bottom: 1px solid $border_light;
|
|
88
102
|
|
|
89
103
|
&:hover, &:focus {
|
|
@@ -92,7 +106,8 @@
|
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
108
|
}
|
|
95
|
-
|
|
109
|
+
&.pb_dropdown_option_selected,
|
|
110
|
+
&.pb_dropdown_option_selected_focused {
|
|
96
111
|
background-color: $primary;
|
|
97
112
|
color: $white;
|
|
98
113
|
[class^="pb_body"],
|
|
@@ -141,41 +156,66 @@
|
|
|
141
156
|
}
|
|
142
157
|
|
|
143
158
|
&.error {
|
|
144
|
-
|
|
159
|
+
.pb_body_kit_negative {
|
|
145
160
|
margin-top: $space_xs / 2;
|
|
146
161
|
}
|
|
147
162
|
|
|
148
|
-
|
|
163
|
+
.dropdown_trigger_wrapper,
|
|
164
|
+
.dropdown_trigger_wrapper_focus,
|
|
165
|
+
.dropdown_trigger_wrapper_focus_select_only,
|
|
166
|
+
.dropdown_trigger_wrapper_select_only {
|
|
149
167
|
border-color: $error;
|
|
150
168
|
box-shadow: none !important;
|
|
151
169
|
}
|
|
152
170
|
}
|
|
153
171
|
}
|
|
154
172
|
|
|
155
|
-
|
|
173
|
+
&.pb_dropdown_default_separators_hidden,
|
|
174
|
+
&.pb_dropdown_subtle_separators_hidden {
|
|
156
175
|
.dropdown_wrapper {
|
|
157
176
|
.pb_dropdown_container {
|
|
158
177
|
|
|
159
|
-
|
|
178
|
+
.pb_dropdown_option,
|
|
179
|
+
.pb_dropdown_option_list,
|
|
180
|
+
.pb_dropdown_option_selected,
|
|
181
|
+
.pb_dropdown_option_focused,
|
|
182
|
+
.pb_dropdown_option_list_focused,
|
|
183
|
+
.pb_dropdown_option_selected_focused {
|
|
160
184
|
border: none;
|
|
161
185
|
}
|
|
162
186
|
}
|
|
163
187
|
}
|
|
164
188
|
}
|
|
165
189
|
|
|
166
|
-
|
|
190
|
+
&.pb_dropdown_subtle,
|
|
191
|
+
&.pb_dropdown_subtle_separators_hidden {
|
|
167
192
|
.dropdown_wrapper {
|
|
168
193
|
.pb_dropdown_container {
|
|
169
194
|
|
|
170
|
-
|
|
195
|
+
.pb_dropdown_option:first-child,
|
|
196
|
+
.pb_dropdown_option_list:first-child,
|
|
197
|
+
.pb_dropdown_option_selected:first-child,
|
|
198
|
+
.pb_dropdown_option_focused:first-child,
|
|
199
|
+
.pb_dropdown_option_list_focused:first-child,
|
|
200
|
+
.pb_dropdown_option_selected_focused:first-child {
|
|
171
201
|
margin-top: $space_xs;
|
|
172
202
|
}
|
|
173
203
|
|
|
174
|
-
|
|
204
|
+
.pb_dropdown_option:last-child,
|
|
205
|
+
.pb_dropdown_option_list:last-child,
|
|
206
|
+
.pb_dropdown_option_selected:last-child,
|
|
207
|
+
.pb_dropdown_option_focused:last-child,
|
|
208
|
+
.pb_dropdown_option_list_focused:last-child,
|
|
209
|
+
.pb_dropdown_option_selected_focused:last-child {
|
|
175
210
|
margin-bottom: $space_xs;
|
|
176
211
|
}
|
|
177
212
|
|
|
178
|
-
|
|
213
|
+
.pb_dropdown_option,
|
|
214
|
+
.pb_dropdown_option_list,
|
|
215
|
+
.pb_dropdown_option_selected,
|
|
216
|
+
.pb_dropdown_option_focused,
|
|
217
|
+
.pb_dropdown_option_list_focused,
|
|
218
|
+
.pb_dropdown_option_selected_focused {
|
|
179
219
|
margin: $space_xs;
|
|
180
220
|
padding: $space_xs;
|
|
181
221
|
border-radius: $border_radius_md;
|
|
@@ -193,24 +233,44 @@
|
|
|
193
233
|
}
|
|
194
234
|
}
|
|
195
235
|
|
|
196
|
-
|
|
236
|
+
.pb_dropdown_option:last-child::after,
|
|
237
|
+
.pb_dropdown_option_list:last-child::after,
|
|
238
|
+
.pb_dropdown_option_selected:last-child::after,
|
|
239
|
+
.pb_dropdown_option_focused:last-child::after,
|
|
240
|
+
.pb_dropdown_option_list_focused:last-child::after,
|
|
241
|
+
.pb_dropdown_option_selected_focused:last-child::after {
|
|
197
242
|
display: none;
|
|
198
243
|
}
|
|
199
244
|
}
|
|
200
245
|
}
|
|
201
246
|
|
|
202
|
-
|
|
247
|
+
&.pb_dropdown_subtle_separators_hidden {
|
|
203
248
|
.dropdown_wrapper {
|
|
204
249
|
.pb_dropdown_container {
|
|
205
|
-
|
|
250
|
+
.pb_dropdown_option:first-child,
|
|
251
|
+
.pb_dropdown_option_list:first-child,
|
|
252
|
+
.pb_dropdown_option_selected:first-child,
|
|
253
|
+
.pb_dropdown_option_focused:first-child,
|
|
254
|
+
.pb_dropdown_option_list_focused:first-child,
|
|
255
|
+
.pb_dropdown_option_selected_focused:first-child {
|
|
206
256
|
margin-top: $space_xs;
|
|
207
257
|
}
|
|
208
258
|
|
|
209
|
-
|
|
259
|
+
.pb_dropdown_option:last-child,
|
|
260
|
+
.pb_dropdown_option_list:last-child,
|
|
261
|
+
.pb_dropdown_option_selected:last-child,
|
|
262
|
+
.pb_dropdown_option_focused:last-child,
|
|
263
|
+
.pb_dropdown_option_list_focused:last-child,
|
|
264
|
+
.pb_dropdown_option_selected_focused:last-child {
|
|
210
265
|
margin-bottom: $space_xs;
|
|
211
266
|
}
|
|
212
267
|
|
|
213
|
-
|
|
268
|
+
.pb_dropdown_option,
|
|
269
|
+
.pb_dropdown_option_list,
|
|
270
|
+
.pb_dropdown_option_selected,
|
|
271
|
+
.pb_dropdown_option_focused,
|
|
272
|
+
.pb_dropdown_option_list_focused,
|
|
273
|
+
.pb_dropdown_option_selected_focused {
|
|
214
274
|
padding: $space_xxs $space_xs;
|
|
215
275
|
margin: $space_xxs $space_xs;
|
|
216
276
|
|
|
@@ -218,7 +278,8 @@
|
|
|
218
278
|
height: 0px;
|
|
219
279
|
}
|
|
220
280
|
|
|
221
|
-
|
|
281
|
+
&.pb_dropdown_option_selected,
|
|
282
|
+
&.pb_dropdown_option_selected_focused {
|
|
222
283
|
border-bottom: none;
|
|
223
284
|
}
|
|
224
285
|
}
|
|
@@ -229,7 +290,10 @@
|
|
|
229
290
|
|
|
230
291
|
&.dark {
|
|
231
292
|
.dropdown_wrapper {
|
|
232
|
-
|
|
293
|
+
.dropdown_trigger_wrapper,
|
|
294
|
+
.dropdown_trigger_wrapper_focus,
|
|
295
|
+
.dropdown_trigger_wrapper_focus_select_only,
|
|
296
|
+
.dropdown_trigger_wrapper_select_only {
|
|
233
297
|
@include pb_body_light_dark;
|
|
234
298
|
background-color: rgba($white, 0.1) !important;
|
|
235
299
|
background: none;
|
|
@@ -244,7 +308,7 @@
|
|
|
244
308
|
[class^="pb_body"] {
|
|
245
309
|
color: $white;
|
|
246
310
|
}
|
|
247
|
-
|
|
311
|
+
&.dropdown_trigger_wrapper_select_only {
|
|
248
312
|
box-shadow: inset 0 -11px 20px rgba($white, 0.05);
|
|
249
313
|
}
|
|
250
314
|
.dropdown_input {
|
|
@@ -254,7 +318,10 @@
|
|
|
254
318
|
}
|
|
255
319
|
|
|
256
320
|
&.error {
|
|
257
|
-
|
|
321
|
+
.dropdown_trigger_wrapper,
|
|
322
|
+
.dropdown_trigger_wrapper_focus,
|
|
323
|
+
.dropdown_trigger_wrapper_focus_select_only,
|
|
324
|
+
.dropdown_trigger_wrapper_select_only {
|
|
258
325
|
border-color: $error_dark;
|
|
259
326
|
}
|
|
260
327
|
}
|
|
@@ -268,19 +335,28 @@
|
|
|
268
335
|
color: $white !important;
|
|
269
336
|
}
|
|
270
337
|
|
|
271
|
-
|
|
338
|
+
.pb_dropdown_option,
|
|
339
|
+
.pb_dropdown_option_list,
|
|
340
|
+
.pb_dropdown_option_selected,
|
|
341
|
+
.pb_dropdown_option_focused,
|
|
342
|
+
.pb_dropdown_option_list_focused,
|
|
343
|
+
.pb_dropdown_option_selected_focused {
|
|
272
344
|
&:hover {
|
|
273
345
|
background-color: $hover_dark;
|
|
274
346
|
}
|
|
275
347
|
|
|
276
|
-
|
|
348
|
+
&.pb_dropdown_option_focused,
|
|
349
|
+
&.pb_dropdown_option_list_focused,
|
|
350
|
+
&.pb_dropdown_option_selected_focused {
|
|
277
351
|
background-color: $hover_dark;
|
|
278
352
|
}
|
|
279
353
|
|
|
280
|
-
|
|
354
|
+
&.pb_dropdown_option_list,
|
|
355
|
+
&.pb_dropdown_option_list_focused {
|
|
281
356
|
border-color: rgba($white, 0.15);
|
|
282
357
|
}
|
|
283
|
-
|
|
358
|
+
&.pb_dropdown_option_selected,
|
|
359
|
+
&.pb_dropdown_option_selected_focused {
|
|
284
360
|
background-color: $primary;
|
|
285
361
|
border-bottom: rgba($white, 0.15);
|
|
286
362
|
}
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
<%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
|
|
91
91
|
<%= form.typeahead :example_typeahead_validation, props: { data: { typeahead_example2: true, user: {} }, label: true, placeholder: "Search for a user", required: true, validation: { message: "Please select a user." } } %>
|
|
92
92
|
<%= form.text_field :example_text_field_validation, props: { label: true, required: true } %>
|
|
93
|
-
<%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field", hidden_inputs: true
|
|
93
|
+
<%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field", hidden_inputs: true } %>
|
|
94
94
|
<%= form.email_field :example_email_field_validation, props: { label: true, required: true } %>
|
|
95
95
|
<%= form.number_field :example_number_field_validation, props: { label: true, required: true } %>
|
|
96
96
|
<%= form.search_field :example_project_number_validation, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
|
|
@@ -2,14 +2,12 @@ import PbEnhancedElement from '../pb_enhanced_element'
|
|
|
2
2
|
import { debounce } from '../utilities/object'
|
|
3
3
|
|
|
4
4
|
// Kit selectors
|
|
5
|
-
const KIT_SELECTOR
|
|
6
|
-
const ERROR_MESSAGE_SELECTOR
|
|
7
|
-
const PHONE_NUMBER_INPUT_SELECTOR = '.pb_phone_number_input'
|
|
5
|
+
const KIT_SELECTOR = '[class^="pb_"][class*="_kit"]'
|
|
6
|
+
const ERROR_MESSAGE_SELECTOR = '.pb_body_kit_negative'
|
|
8
7
|
|
|
9
8
|
// Validation selectors
|
|
10
|
-
const FORM_SELECTOR
|
|
11
|
-
const REQUIRED_FIELDS_SELECTOR
|
|
12
|
-
const PHONE_NUMBER_VALIDATION_ERROR_SELECTOR = '[data-pb-phone-validation-error="true"]'
|
|
9
|
+
const FORM_SELECTOR = 'form[data-pb-form-validation="true"]'
|
|
10
|
+
const REQUIRED_FIELDS_SELECTOR = 'input[required],textarea[required],select[required]'
|
|
13
11
|
|
|
14
12
|
const FIELD_EVENTS = [
|
|
15
13
|
'change',
|
|
@@ -30,14 +28,6 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
30
28
|
}, 250), false)
|
|
31
29
|
})
|
|
32
30
|
})
|
|
33
|
-
|
|
34
|
-
// Add event listener to check for phone number validation errors
|
|
35
|
-
this.element.addEventListener('submit', (event) => {
|
|
36
|
-
if (this.hasPhoneNumberValidationErrors()) {
|
|
37
|
-
event.preventDefault()
|
|
38
|
-
return false
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
31
|
}
|
|
42
32
|
|
|
43
33
|
validateFormField(event) {
|
|
@@ -55,18 +45,10 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
55
45
|
|
|
56
46
|
showValidationMessage(target) {
|
|
57
47
|
const { parentElement } = target
|
|
58
|
-
const kitElement = parentElement.closest(KIT_SELECTOR)
|
|
59
|
-
|
|
60
|
-
// Skip error message container for Phone Number Input as it handles its own errors
|
|
61
|
-
// as the closest kitElement is "pb_text_input_kit mb_sm" for Phone Number Input,
|
|
62
|
-
// we target the parent element of kitElement to check
|
|
63
|
-
if (kitElement && kitElement.parentElement.matches(PHONE_NUMBER_INPUT_SELECTOR)) {
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
48
|
|
|
67
49
|
// ensure clean error message state
|
|
68
50
|
this.clearError(target)
|
|
69
|
-
|
|
51
|
+
parentElement.closest(KIT_SELECTOR).classList.add('error')
|
|
70
52
|
|
|
71
53
|
// set the error message element
|
|
72
54
|
const errorMessageContainer = this.errorMessageContainer
|
|
@@ -81,23 +63,9 @@ class PbFormValidation extends PbEnhancedElement {
|
|
|
81
63
|
|
|
82
64
|
clearError(target) {
|
|
83
65
|
const { parentElement } = target
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
kitElement.classList.remove('error')
|
|
88
|
-
|
|
89
|
-
// Only remove error message container for non-Phone Number Input kits
|
|
90
|
-
if (!kitElement.matches(PHONE_NUMBER_INPUT_SELECTOR)) {
|
|
91
|
-
const errorMessageContainer = parentElement.querySelector(ERROR_MESSAGE_SELECTOR)
|
|
92
|
-
if (errorMessageContainer) errorMessageContainer.remove()
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check if there are phone number input errors
|
|
98
|
-
hasPhoneNumberValidationErrors() {
|
|
99
|
-
const phoneNumberErrors = this.element.querySelectorAll(PHONE_NUMBER_VALIDATION_ERROR_SELECTOR)
|
|
100
|
-
return phoneNumberErrors.length > 0
|
|
66
|
+
parentElement.closest(KIT_SELECTOR).classList.remove('error')
|
|
67
|
+
const errorMessageContainer = parentElement.querySelector(ERROR_MESSAGE_SELECTOR)
|
|
68
|
+
if (errorMessageContainer) errorMessageContainer.remove()
|
|
101
69
|
}
|
|
102
70
|
|
|
103
71
|
get errorMessageContainer() {
|
|
@@ -110,35 +110,18 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
110
110
|
|
|
111
111
|
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
112
112
|
const itiRef = useRef<any>(null);
|
|
113
|
-
const wrapperRef = useRef<HTMLDivElement | null>(null); // Add wrapper ref
|
|
114
113
|
const [inputValue, setInputValue] = useState(value)
|
|
115
114
|
const [error, setError] = useState(props.error)
|
|
116
115
|
const [dropDownIsOpen, setDropDownIsOpen] = useState(false)
|
|
117
116
|
const [selectedData, setSelectedData] = useState()
|
|
118
117
|
const [hasTyped, setHasTyped] = useState(false)
|
|
119
118
|
|
|
120
|
-
// Function to update validation state on the wrapper element
|
|
121
|
-
// Only applies when input is required
|
|
122
|
-
const updateValidationState = (hasError: boolean) => {
|
|
123
|
-
if (wrapperRef.current && required) {
|
|
124
|
-
if (hasError) {
|
|
125
|
-
wrapperRef.current.setAttribute('data-pb-phone-validation-error', 'true')
|
|
126
|
-
} else {
|
|
127
|
-
wrapperRef.current.removeAttribute('data-pb-phone-validation-error')
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
119
|
useEffect(() => {
|
|
133
|
-
|
|
134
|
-
if (hasError) {
|
|
120
|
+
if ((error ?? '').length > 0) {
|
|
135
121
|
onValidate(false)
|
|
136
122
|
} else {
|
|
137
123
|
onValidate(true)
|
|
138
124
|
}
|
|
139
|
-
|
|
140
|
-
// Update validation state whenever error changes
|
|
141
|
-
updateValidationState(hasError)
|
|
142
125
|
}, [error, onValidate])
|
|
143
126
|
|
|
144
127
|
/*
|
|
@@ -152,10 +135,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
152
135
|
setInputValue("")
|
|
153
136
|
setError("")
|
|
154
137
|
setHasTyped(false)
|
|
155
|
-
// Only clear validation state if field was required
|
|
156
|
-
if (required) {
|
|
157
|
-
updateValidationState(false)
|
|
158
|
-
}
|
|
159
138
|
},
|
|
160
139
|
inputNode() {
|
|
161
140
|
return inputRef.current
|
|
@@ -185,13 +164,6 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
185
164
|
|
|
186
165
|
const validateTooShortNumber = (itiInit: any) => {
|
|
187
166
|
if (!itiInit) return
|
|
188
|
-
|
|
189
|
-
// If field is empty, don't show "too short" error
|
|
190
|
-
if (!inputValue || inputValue.trim() === '') {
|
|
191
|
-
setError('')
|
|
192
|
-
return false
|
|
193
|
-
}
|
|
194
|
-
|
|
195
167
|
if (itiInit.getValidationError() === ValidationError.TooShort) {
|
|
196
168
|
return showFormattedError('too short')
|
|
197
169
|
} else {
|
|
@@ -240,26 +212,8 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
240
212
|
}
|
|
241
213
|
}
|
|
242
214
|
|
|
243
|
-
// Validation for required empty fields
|
|
244
|
-
const validateRequiredField = () => {
|
|
245
|
-
if (required && (!inputValue || inputValue.trim() === '')) {
|
|
246
|
-
setError('Missing phone number')
|
|
247
|
-
return true
|
|
248
|
-
}
|
|
249
|
-
return false
|
|
250
|
-
}
|
|
251
215
|
|
|
252
216
|
const validateErrors = () => {
|
|
253
|
-
// If field is empty, only show required field error if applicable
|
|
254
|
-
if (!inputValue || inputValue.trim() === '') {
|
|
255
|
-
if (validateRequiredField()) return
|
|
256
|
-
// Clear any existing errors if field is empty and not required
|
|
257
|
-
if (!required) {
|
|
258
|
-
setError('')
|
|
259
|
-
}
|
|
260
|
-
return
|
|
261
|
-
}
|
|
262
|
-
|
|
263
217
|
if (!hasTyped && !error) return
|
|
264
218
|
|
|
265
219
|
if (itiRef.current) isValid(itiRef.current.isValidNumber())
|
|
@@ -356,10 +310,7 @@ const PhoneNumberInput = (props: PhoneNumberInputProps, ref?: React.Ref<unknown>
|
|
|
356
310
|
value: inputValue
|
|
357
311
|
}
|
|
358
312
|
|
|
359
|
-
let wrapperProps: Record<string, unknown> = {
|
|
360
|
-
className: classes,
|
|
361
|
-
ref: wrapperRef // Add ref to wrapper
|
|
362
|
-
}
|
|
313
|
+
let wrapperProps: Record<string, unknown> = { className: classes }
|
|
363
314
|
|
|
364
315
|
if (!isEmpty(aria)) textInputProps = {...textInputProps, ...ariaProps}
|
|
365
316
|
if (!isEmpty(data)) wrapperProps = {...wrapperProps, ...dataProps}
|