playbook_ui 14.11.1.pre.alpha.play1724darkmodeauditmap5437 → 14.11.1.pre.alpha.play17725372
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_date_picker/docs/_date_picker_default_date.md +1 -1
- data/app/pb_kits/playbook/pb_draggable/docs/example.yml +0 -1
- data/app/pb_kits/playbook/pb_map/_map.scss +0 -3
- data/app/pb_kits/playbook/pb_map/docs/_map_default.jsx +4 -4
- data/app/pb_kits/playbook/pb_map/docs/_map_with_custom_button.jsx +7 -7
- data/app/pb_kits/playbook/pb_map/docs/_map_with_plugin.jsx +5 -5
- data/app/pb_kits/playbook/pb_selectable_list/selectable_list.html.erb +3 -17
- data/app/pb_kits/playbook/pb_selectable_list/selectable_list.rb +0 -3
- data/app/pb_kits/playbook/pb_selectable_list/selectable_list_item.html.erb +4 -11
- data/app/pb_kits/playbook/pb_selectable_list/selectable_list_item.rb +0 -3
- data/app/pb_kits/playbook/pb_text_input/docs/_text_input_mask.html.erb +50 -0
- data/app/pb_kits/playbook/pb_text_input/docs/_text_input_mask_rails.md +3 -0
- data/app/pb_kits/playbook/pb_text_input/docs/example.yml +2 -1
- data/app/pb_kits/playbook/pb_text_input/index.js +103 -0
- data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +4 -0
- data/app/pb_kits/playbook/pb_text_input/text_input.rb +47 -4
- data/dist/playbook-doc.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +5 -18
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list_rails.html.erb +0 -38
- data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_selectable_list_rails.md +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 605e8fba05e7ad729b24918b880cd6d2c3b42c040d63b72ce79a718a6b5f5078
|
4
|
+
data.tar.gz: 2c576ffcf40d00c75f2a8fbe09c1987ea33956b52d4db438731e9b94e16fa599
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d9aeb7d6aca91c1698da83d050f0073fabf03158f5f6006ebe5d71ae326c48525b2de9f2ea0ae6dc20fb08337bf584e4714cec299ba80d27fb9e1990f43d318
|
7
|
+
data.tar.gz: 60dafa94ed713fba0242a8b9e297963d3f422191c5deb77332efcfdc9f37c47110a588fd80605934a5e445a83ed585614f4db298f46af99fdb89f468f6aa83a6
|
@@ -2,4 +2,4 @@ The `defaultDate`/`default_date` prop has a null or empty string value by defaul
|
|
2
2
|
|
3
3
|
If you use a Date object without UTC time standardization the Date Picker kit may misinterpret that date as yesterdays date (consequence of timezone differentials and the Javascript Date Object constructor). See [this GitHub issue for more information](https://github.com/powerhome/playbook/issues/1167) and the anti-pattern examples below.
|
4
4
|
|
5
|
-
|
5
|
+
|
@@ -3,7 +3,6 @@
|
|
3
3
|
@import "../tokens/shadows";
|
4
4
|
@import "../tokens/border_radius";
|
5
5
|
@import "./_pb_map_button_mixin.scss";
|
6
|
-
@import "../tokens/titles";
|
7
6
|
|
8
7
|
[class*="pb_map"] {
|
9
8
|
.pb_map-custom-button {
|
@@ -160,7 +159,6 @@
|
|
160
159
|
}
|
161
160
|
|
162
161
|
.maplibregl-popup-content {
|
163
|
-
@include pb_title_4;
|
164
162
|
padding: $space_sm;
|
165
163
|
background-color: $card_light;
|
166
164
|
box-shadow: $shadow_deeper;
|
@@ -196,7 +194,6 @@
|
|
196
194
|
}
|
197
195
|
|
198
196
|
.maplibregl-popup-content {
|
199
|
-
@include pb_title_4;
|
200
197
|
background-color: $bg_dark;
|
201
198
|
color: $text_dk_default;
|
202
199
|
}
|
@@ -25,12 +25,12 @@ const MapDefault = (props) => {
|
|
25
25
|
new maplibregl.Marker({
|
26
26
|
color: mapTheme.marker,
|
27
27
|
}).setLngLat(defaultPosition)
|
28
|
-
.setPopup(new maplibregl.Popup({closeButton: false}).setHTML(
|
28
|
+
.setPopup(new maplibregl.Popup({closeButton: false}).setHTML(`<h4 class="pb_title_kit_size_4">Hello World!</h4>`)) // add popup
|
29
29
|
.addTo(map);
|
30
30
|
|
31
31
|
// disable map zoom when using scroll
|
32
32
|
map.scrollZoom.disable();
|
33
|
-
|
33
|
+
|
34
34
|
//add attributioncontrols
|
35
35
|
map.addControl(new maplibregl.AttributionControl({
|
36
36
|
compact: true
|
@@ -49,7 +49,7 @@ const MapDefault = (props) => {
|
|
49
49
|
|
50
50
|
}, [])
|
51
51
|
|
52
|
-
return (
|
52
|
+
return (
|
53
53
|
<Map flyTo
|
54
54
|
flyToClick={()=> {handleFlyTo(mapInstance)}}
|
55
55
|
zoomBtns
|
@@ -63,7 +63,7 @@ return (
|
|
63
63
|
position: 'absolute',
|
64
64
|
left: 0,
|
65
65
|
right: 0,
|
66
|
-
top: 0,
|
66
|
+
top: 0,
|
67
67
|
bottom: 0,
|
68
68
|
}}
|
69
69
|
/>
|
@@ -25,12 +25,12 @@ const MapWithCustomButton = (props) => {
|
|
25
25
|
new maplibregl.Marker({
|
26
26
|
color: mapTheme.marker,
|
27
27
|
}).setLngLat(defaultPosition)
|
28
|
-
.setPopup(new maplibregl.Popup({closeButton: false}).setHTML(
|
28
|
+
.setPopup(new maplibregl.Popup({closeButton: false}).setHTML(`<h4 class="pb_title_kit_size_4">Hello World!</h4>`)) // add popup
|
29
29
|
.addTo(map);
|
30
30
|
|
31
31
|
// disable map zoom when using scroll
|
32
32
|
map.scrollZoom.disable();
|
33
|
-
|
33
|
+
|
34
34
|
//add attributioncontrols
|
35
35
|
map.addControl(new maplibregl.AttributionControl({
|
36
36
|
compact: true
|
@@ -49,8 +49,8 @@ const MapWithCustomButton = (props) => {
|
|
49
49
|
|
50
50
|
}, [])
|
51
51
|
|
52
|
-
return (
|
53
|
-
<Map
|
52
|
+
return (
|
53
|
+
<Map
|
54
54
|
{...props}
|
55
55
|
>
|
56
56
|
<Map.Controls flyTo
|
@@ -59,10 +59,10 @@ return (
|
|
59
59
|
zoomInClick={() => {handleZoomIn(mapInstance)}}
|
60
60
|
zoomOutClick={()=> {handleZoomOut(mapInstance)}}
|
61
61
|
>
|
62
|
-
<MapCustomButton icon="home"
|
62
|
+
<MapCustomButton icon="home"
|
63
63
|
onClick={() => alert("button clicked!")}
|
64
64
|
/>
|
65
|
-
<MapCustomButton icon="search"
|
65
|
+
<MapCustomButton icon="search"
|
66
66
|
onClick={() => alert("button clicked!")}
|
67
67
|
/>
|
68
68
|
</Map.Controls>
|
@@ -72,7 +72,7 @@ return (
|
|
72
72
|
position: 'absolute',
|
73
73
|
left: 0,
|
74
74
|
right: 0,
|
75
|
-
top: 0,
|
75
|
+
top: 0,
|
76
76
|
bottom: 0,
|
77
77
|
}}
|
78
78
|
/>
|
@@ -25,7 +25,7 @@ const MapWithPlugin = (props) => {
|
|
25
25
|
new maplibregl.Marker({
|
26
26
|
color: mapTheme.marker,
|
27
27
|
}).setLngLat(defaultPosition)
|
28
|
-
.setPopup(new maplibregl.Popup({className: 'map_popup', closeButton: false}).setHTML(
|
28
|
+
.setPopup(new maplibregl.Popup({className: 'map_popup', closeButton: false}).setHTML(`<h4 class="pb_title_kit_size_4">Hello World!</h4>`)) // add popup
|
29
29
|
.addTo(map);
|
30
30
|
|
31
31
|
//add maplibre default zoom controls
|
@@ -48,7 +48,7 @@ const MapWithPlugin = (props) => {
|
|
48
48
|
map.addControl(new maplibregl.AttributionControl({
|
49
49
|
compact: true
|
50
50
|
}));
|
51
|
-
|
51
|
+
|
52
52
|
//set map instance
|
53
53
|
setMapInstance(map)
|
54
54
|
}
|
@@ -62,8 +62,8 @@ const MapWithPlugin = (props) => {
|
|
62
62
|
}, [])
|
63
63
|
|
64
64
|
|
65
|
-
|
66
|
-
return (
|
65
|
+
|
66
|
+
return (
|
67
67
|
<Map flyTo
|
68
68
|
flyToClick={()=> {handleFlyTo(mapInstance)}}
|
69
69
|
zoomBtns
|
@@ -77,7 +77,7 @@ return (
|
|
77
77
|
position: 'absolute',
|
78
78
|
left: 0,
|
79
79
|
right: 0,
|
80
|
-
top: 0,
|
80
|
+
top: 0,
|
81
81
|
bottom: 0,
|
82
82
|
}}
|
83
83
|
/>
|
@@ -4,23 +4,9 @@
|
|
4
4
|
data: object.data,
|
5
5
|
id: object.id,
|
6
6
|
**combined_html_options) do %>
|
7
|
-
|
8
|
-
|
9
|
-
<%= pb_rails("
|
10
|
-
<%= pb_rails("list", props: {ordered: false}) do %>
|
11
|
-
<% object.items.each do |item| %>
|
12
|
-
<%= pb_rails("draggable/draggable_item", props: {drag_id: item[:drag_id]}) do %>
|
13
|
-
<%= pb_rails("selectable_list/selectable_list_item", props: item.merge(variant: object.variant, id: object.get_id(item), drag_id: item[:drag_id]) )%>
|
14
|
-
<% end %>
|
15
|
-
<% end %>
|
16
|
-
<% end %>
|
17
|
-
<% end %>
|
18
|
-
<% end %>
|
19
|
-
<% else %>
|
20
|
-
<%= pb_rails("list") do %>
|
21
|
-
<% object.items.each do |item| %>
|
22
|
-
<%= pb_rails("selectable_list/selectable_list_item", props: item.merge(variant: object.variant, id: object.get_id(item)) )%>
|
23
|
-
<% end %>
|
7
|
+
<%= pb_rails("list") do %>
|
8
|
+
<% object.items.each do |item| %>
|
9
|
+
<%= pb_rails("selectable_list/selectable_list_item", props: item.merge(variant: object.variant, id: object.get_id(item)) )%>
|
24
10
|
<% end %>
|
25
11
|
<% end %>
|
26
12
|
<% end %>
|
@@ -4,13 +4,6 @@
|
|
4
4
|
data: object.data,
|
5
5
|
id: object.id,
|
6
6
|
**combined_html_options) do %>
|
7
|
-
<% if object.drag_id && object.drag_handle %>
|
8
|
-
<span style="vertical-align: middle;">
|
9
|
-
<%= pb_rails("body") do %>
|
10
|
-
<svg width="auto" height="auto" viewBox="0 0 31 25" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor" class="pb_custom_icon svg-inline--fa vertical_align_middle svg_fw"><path d="M12.904 6.355a1.48 1.48 0 01-1.5-1.5c0-.796.656-1.5 1.5-1.5.797 0 1.5.704 1.5 1.5 0 .844-.703 1.5-1.5 1.5zm0 7.5a1.48 1.48 0 01-1.5-1.5c0-.796.656-1.5 1.5-1.5.797 0 1.5.704 1.5 1.5 0 .844-.703 1.5-1.5 1.5zm1.5 6c0 .844-.703 1.5-1.5 1.5a1.48 1.48 0 01-1.5-1.5c0-.796.656-1.5 1.5-1.5.797 0 1.5.704 1.5 1.5zm4.5-13.5a1.48 1.48 0 01-1.5-1.5c0-.796.657-1.5 1.5-1.5.797 0 1.5.704 1.5 1.5 0 .844-.703 1.5-1.5 1.5zm1.5 6c0 .844-.703 1.5-1.5 1.5a1.48 1.48 0 01-1.5-1.5c0-.796.657-1.5 1.5-1.5.797 0 1.5.704 1.5 1.5zm-1.5 9a1.48 1.48 0 01-1.5-1.5c0-.796.657-1.5 1.5-1.5.797 0 1.5.704 1.5 1.5 0 .844-.703 1.5-1.5 1.5z" fill="#242B42"></path></svg>
|
11
|
-
<% end %>
|
12
|
-
</span>
|
13
|
-
<% end %>
|
14
7
|
<% if object.variant == "radio"%>
|
15
8
|
<%= pb_rails("radio", props: { text: object.text, checked: object.checked, input_options: object.input_options } ) %>
|
16
9
|
<% if content.present? %>
|
@@ -26,10 +19,10 @@
|
|
26
19
|
<% if object.variant == "checkbox"%>
|
27
20
|
<script>
|
28
21
|
var checkboxElement = document.querySelector("#<%=object.id%> input[type=checkbox]")
|
29
|
-
|
22
|
+
|
30
23
|
checkboxElement.addEventListener("change", (evt) => {
|
31
24
|
var listItemElement = document.querySelector("#<%=object.id%>")
|
32
|
-
|
25
|
+
|
33
26
|
if (evt.target.checked) {
|
34
27
|
listItemElement.classList.add("checked_item");
|
35
28
|
} else {
|
@@ -41,9 +34,9 @@
|
|
41
34
|
<script>
|
42
35
|
var radioElement = document.querySelector("#<%=object.id%> input[type=radio]")
|
43
36
|
|
44
|
-
radioElement.addEventListener("change", () => {
|
37
|
+
radioElement.addEventListener("change", () => {
|
45
38
|
var radios = radioElement.closest("ul").querySelectorAll("input[type=radio]")
|
46
|
-
|
39
|
+
|
47
40
|
radios.forEach((radio) => {
|
48
41
|
if (radio.checked) {
|
49
42
|
radio.closest("li").classList.add("checked_item");
|
@@ -6,9 +6,6 @@ module Playbook
|
|
6
6
|
prop :tabindex
|
7
7
|
prop :checked, type: Playbook::Props::Boolean,
|
8
8
|
default: false
|
9
|
-
prop :drag_handle, type: Playbook::Props::Boolean,
|
10
|
-
default: true
|
11
|
-
prop :drag_id, type: Playbook::Props::String
|
12
9
|
prop :name, type: Playbook::Props::String
|
13
10
|
prop :text, type: Playbook::Props::String
|
14
11
|
prop :value, type: Playbook::Props::String
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
<%= pb_rails("title", props: { text: "Input Masks", size: 4, margin_bottom: "md" }) %>
|
5
|
+
|
6
|
+
<div class="pb--doc-demo-elements">
|
7
|
+
<%= pb_rails("text_input", props: {
|
8
|
+
label: "Currency",
|
9
|
+
mask: "currency",
|
10
|
+
margin_bottom: "md",
|
11
|
+
name: "currency_name"
|
12
|
+
}) %>
|
13
|
+
|
14
|
+
<%= pb_rails("text_input", props: {
|
15
|
+
label: "ZIP Code",
|
16
|
+
mask: "zip_code",
|
17
|
+
margin_bottom: "md",
|
18
|
+
}) %>
|
19
|
+
|
20
|
+
<%= pb_rails("text_input", props: {
|
21
|
+
label: "Postal Code",
|
22
|
+
mask: "postal_code",
|
23
|
+
placeholder: "12345-6789",
|
24
|
+
margin_bottom: "md",
|
25
|
+
}) %>
|
26
|
+
|
27
|
+
<%= pb_rails("text_input", props: {
|
28
|
+
label: "Social Security Number",
|
29
|
+
mask: "ssn",
|
30
|
+
margin_bottom: "md",
|
31
|
+
}) %>
|
32
|
+
|
33
|
+
<%= pb_rails("title" , props: {
|
34
|
+
text: "Hidden Input Under The Hood",
|
35
|
+
padding_bottom: "sm"
|
36
|
+
})%>
|
37
|
+
|
38
|
+
<%= pb_rails("text_input", props: {
|
39
|
+
label: "Currency",
|
40
|
+
mask: "currency",
|
41
|
+
margin_bottom: "md",
|
42
|
+
name: "currency_name",
|
43
|
+
id: "example-currency"
|
44
|
+
}) %>
|
45
|
+
|
46
|
+
<style>
|
47
|
+
#example-currency-sanitized {display: flex !important;}
|
48
|
+
</style>
|
49
|
+
|
50
|
+
</div>
|
@@ -8,6 +8,7 @@ examples:
|
|
8
8
|
- text_input_inline: Inline
|
9
9
|
- text_input_no_label: No Label
|
10
10
|
- text_input_options: Input Options
|
11
|
+
- text_input_mask: Mask
|
11
12
|
react:
|
12
13
|
- text_input_default: Default
|
13
14
|
- text_input_error: With Error
|
@@ -23,4 +24,4 @@ examples:
|
|
23
24
|
- text_input_error_swift: With Error
|
24
25
|
- text_input_disabled_swift: Disabled
|
25
26
|
- text_input_add_on_swift: Add On
|
26
|
-
- text_input_props_swift: ""
|
27
|
+
- text_input_props_swift: ""
|
@@ -0,0 +1,103 @@
|
|
1
|
+
export default class PbTextInput {
|
2
|
+
static start() {
|
3
|
+
const inputElements = document.querySelectorAll('[data-pb-input-mask="true"]');
|
4
|
+
|
5
|
+
inputElements.forEach((inputElement) => {
|
6
|
+
inputElement.addEventListener("input", (event) => {
|
7
|
+
const maskType = inputElement.getAttribute("mask");
|
8
|
+
const cursorPosition = inputElement.selectionStart;
|
9
|
+
|
10
|
+
let rawValue = event.target.value;
|
11
|
+
let formattedValue = rawValue;
|
12
|
+
|
13
|
+
// Apply formatting based on the mask type
|
14
|
+
switch (maskType) {
|
15
|
+
case "currency":
|
16
|
+
formattedValue = formatCurrency(rawValue);
|
17
|
+
break;
|
18
|
+
case "ssn":
|
19
|
+
formattedValue = formatSSN(rawValue);
|
20
|
+
break;
|
21
|
+
case "postal_code":
|
22
|
+
formattedValue = formatPostalCode(rawValue);
|
23
|
+
break;
|
24
|
+
case "zip_code":
|
25
|
+
formattedValue = formatZipCode(rawValue);
|
26
|
+
break;
|
27
|
+
}
|
28
|
+
|
29
|
+
// Update the sanitized input field in the same wrapper
|
30
|
+
const sanitizedInput = inputElement
|
31
|
+
.closest(".text_input_wrapper")
|
32
|
+
?.querySelector('[data="sanitized-pb-input"]');
|
33
|
+
|
34
|
+
if (sanitizedInput) {
|
35
|
+
switch (maskType) {
|
36
|
+
case "ssn":
|
37
|
+
sanitizedInput.value = sanitizeSSN(formattedValue);
|
38
|
+
break;
|
39
|
+
case "currency":
|
40
|
+
sanitizedInput.value = sanitizeCurrency(formattedValue);
|
41
|
+
break;
|
42
|
+
default:
|
43
|
+
sanitizedInput.value = formattedValue;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
inputElement.value = formattedValue;
|
48
|
+
setCursorPosition(inputElement, cursorPosition, rawValue, formattedValue);
|
49
|
+
});
|
50
|
+
});
|
51
|
+
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
function formatCurrency(value) {
|
56
|
+
const numericValue = value.replace(/[^0-9]/g, "").slice(0, 15);
|
57
|
+
|
58
|
+
if (!numericValue) return "";
|
59
|
+
|
60
|
+
const dollars = parseFloat((parseInt(numericValue) / 100).toFixed(2));
|
61
|
+
if (dollars === 0) return "";
|
62
|
+
|
63
|
+
return new Intl.NumberFormat("en-US", {
|
64
|
+
style: "currency",
|
65
|
+
currency: "USD",
|
66
|
+
maximumFractionDigits: 2,
|
67
|
+
}).format(dollars);
|
68
|
+
}
|
69
|
+
|
70
|
+
function formatSSN(value) {
|
71
|
+
const cleaned = value.replace(/\D/g, "").slice(0, 9);
|
72
|
+
return cleaned
|
73
|
+
.replace(/(\d{5})(?=\d)/, "$1-")
|
74
|
+
.replace(/(\d{3})(?=\d)/, "$1-");
|
75
|
+
}
|
76
|
+
|
77
|
+
function formatZipCode(value) {
|
78
|
+
return value.replace(/\D/g, "").slice(0, 5);
|
79
|
+
}
|
80
|
+
|
81
|
+
function formatPostalCode(value) {
|
82
|
+
const cleaned = value.replace(/\D/g, "").slice(0, 9);
|
83
|
+
return cleaned.replace(/(\d{5})(?=\d)/, "$1-");
|
84
|
+
}
|
85
|
+
|
86
|
+
function sanitizeSSN(input) {
|
87
|
+
return input.replace(/\D/g, "");
|
88
|
+
}
|
89
|
+
|
90
|
+
function sanitizeCurrency(input) {
|
91
|
+
return input.replace(/[$,]/g, "");
|
92
|
+
}
|
93
|
+
|
94
|
+
// function to set cursor position
|
95
|
+
function setCursorPosition(inputElement, cursorPosition, rawValue, formattedValue) {
|
96
|
+
const difference = formattedValue.length - rawValue.length;
|
97
|
+
|
98
|
+
const newPosition = Math.max(0, cursorPosition + difference);
|
99
|
+
|
100
|
+
requestAnimationFrame(() => {
|
101
|
+
inputElement.setSelectionRange(newPosition, newPosition);
|
102
|
+
});
|
103
|
+
}
|
@@ -13,9 +13,13 @@
|
|
13
13
|
<%= pb_rails("text_input/add_on", props: object.add_on_props) do %>
|
14
14
|
<%= input_tag %>
|
15
15
|
<% end %>
|
16
|
+
<% elsif mask.present? %>
|
17
|
+
<%= input_tag %>
|
18
|
+
<%= tag(:input, data: "sanitized-pb-input", id: sanitized_id, name: object.name, style: "display: none;") %>
|
16
19
|
<% else %>
|
17
20
|
<%= input_tag %>
|
18
21
|
<% end %>
|
19
22
|
<%= pb_rails("body", props: {dark: object.dark, status: "negative", text: object.error}) if object.error %>
|
20
23
|
<% end %>
|
21
24
|
<% end %>
|
25
|
+
|
@@ -4,6 +4,22 @@
|
|
4
4
|
module Playbook
|
5
5
|
module PbTextInput
|
6
6
|
class TextInput < Playbook::KitBase
|
7
|
+
VALID_MASKS = %w[currency zipCode postalCode ssn].freeze
|
8
|
+
|
9
|
+
MASK_PATTERNS = {
|
10
|
+
"currency" => '^\$\d{1,3}(?:,\d{3})*(?:\.\d{2})?$',
|
11
|
+
"zip_code" => '\d{5}',
|
12
|
+
"postal_code" => '\d{5}-\d{4}',
|
13
|
+
"ssn" => '\d{3}-\d{2}-\d{4}',
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
MASK_PLACEHOLDERS = {
|
17
|
+
"currency" => "$0.00",
|
18
|
+
"zip_code" => "12345",
|
19
|
+
"postal_code" => "12345-6789",
|
20
|
+
"ssn" => "123-45-6789",
|
21
|
+
}.freeze
|
22
|
+
|
7
23
|
prop :autocomplete, type: Playbook::Props::Boolean,
|
8
24
|
default: true
|
9
25
|
prop :disabled, type: Playbook::Props::Boolean,
|
@@ -25,6 +41,9 @@ module Playbook
|
|
25
41
|
prop :add_on, type: Playbook::Props::NestedProps,
|
26
42
|
nested_kit: Playbook::PbTextInput::AddOn
|
27
43
|
|
44
|
+
prop :mask, type: Playbook::Props::String,
|
45
|
+
default: nil
|
46
|
+
|
28
47
|
def classname
|
29
48
|
default_margin_bottom = margin_bottom.present? ? "" : " mb_sm"
|
30
49
|
generate_classname("pb_text_input_kit") + default_margin_bottom + error_class + inline_class
|
@@ -46,6 +65,10 @@ module Playbook
|
|
46
65
|
{ dark: dark }.merge(add_on || {})
|
47
66
|
end
|
48
67
|
|
68
|
+
def sanitized_id
|
69
|
+
"#{object.id}-sanitized" if id.present?
|
70
|
+
end
|
71
|
+
|
49
72
|
private
|
50
73
|
|
51
74
|
def all_input_options
|
@@ -55,12 +78,13 @@ module Playbook
|
|
55
78
|
data: validation_data,
|
56
79
|
disabled: disabled,
|
57
80
|
id: input_options.dig(:id) || id,
|
58
|
-
name: name,
|
59
|
-
pattern: validation_pattern,
|
60
|
-
placeholder: placeholder,
|
81
|
+
name: mask.present? ? "" : name,
|
82
|
+
pattern: validation_pattern || mask_pattern,
|
83
|
+
placeholder: placeholder || mask_placeholder,
|
61
84
|
required: required,
|
62
85
|
type: type,
|
63
86
|
value: value,
|
87
|
+
mask: mask,
|
64
88
|
}.merge(input_options)
|
65
89
|
end
|
66
90
|
|
@@ -75,7 +99,7 @@ module Playbook
|
|
75
99
|
def validation_data
|
76
100
|
fields = input_options.dig(:data) || {}
|
77
101
|
fields[:message] = validation_message unless validation_message.blank?
|
78
|
-
fields
|
102
|
+
mask ? fields.merge(pb_input_mask: true) : fields
|
79
103
|
end
|
80
104
|
|
81
105
|
def error_class
|
@@ -85,6 +109,25 @@ module Playbook
|
|
85
109
|
def inline_class
|
86
110
|
inline ? " inline" : ""
|
87
111
|
end
|
112
|
+
|
113
|
+
def mask_data
|
114
|
+
return {} unless mask
|
115
|
+
raise ArgumentError, "mask must be one of: #{VALID_MASKS.join(', ')}" unless VALID_MASKS.include?(mask)
|
116
|
+
|
117
|
+
{ mask: mask }
|
118
|
+
end
|
119
|
+
|
120
|
+
def mask_pattern
|
121
|
+
return nil unless mask
|
122
|
+
|
123
|
+
MASK_PATTERNS[mask]
|
124
|
+
end
|
125
|
+
|
126
|
+
def mask_placeholder
|
127
|
+
return nil unless mask
|
128
|
+
|
129
|
+
MASK_PLACEHOLDERS[mask]
|
130
|
+
end
|
88
131
|
end
|
89
132
|
end
|
90
133
|
end
|