playbook_ui 14.11.1.pre.alpha.play1724darkmodeauditmap5437 → 14.11.1.pre.alpha.play17725372

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd2c4386840b8ed40fbdcf38b8ee5bdfb9ae12835b7514488a3c45e447e979d2
4
- data.tar.gz: 556917dff9d652fa0f85daba1bbc773edb9565d8a073e58a6ac1762cbe062936
3
+ metadata.gz: 605e8fba05e7ad729b24918b880cd6d2c3b42c040d63b72ce79a718a6b5f5078
4
+ data.tar.gz: 2c576ffcf40d00c75f2a8fbe09c1987ea33956b52d4db438731e9b94e16fa599
5
5
  SHA512:
6
- metadata.gz: af6e2d7210fb12307df00c21f083c04c39e7ff2b4a4f7795c1e48b045d8b7f94b9d1b02695e2c536cf989c000f5df907bcf581af6d60766f9061236ce41cc034
7
- data.tar.gz: c357bbc958caaa66a17d3c5f965987fa0eb6f97bbdd3b11c2999faa0b1b171ae0f0e1bd8f4500337840feeb672bef03fb061a2a13351b8cc0e35521fb1352ff2
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
- You can leverage the `defaultDate`/`default_date` prop with custom logic in your filter or controller files where the determination of the default value changes based on user interaction. The page can load with an initial default date picker value or placeholder text, then after filter submission save the submitted values as the "new default" (via state or params).
5
+
@@ -11,7 +11,6 @@ examples:
11
11
  rails:
12
12
  - draggable_default_rails: Default
13
13
  - draggable_with_list_rails: Draggable with List Kit
14
- - draggable_with_selectable_list_rails: Draggable with SelectableList Kit
15
14
  - draggable_with_cards_rails: Draggable with Cards
16
15
 
17
16
 
@@ -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('Hello World!')) // add popup
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('Hello World!')) // add popup
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('Hello World!')) // add popup
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
- <% if enable_drag %>
8
- <%= pb_rails("draggable", props: {initial_items: object.items}) do %>
9
- <%= pb_rails("draggable/draggable_container") do %>
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 %>
@@ -14,9 +14,6 @@ module Playbook
14
14
  prop :items, type: Playbook::Props::Array,
15
15
  default: []
16
16
 
17
- prop :enable_drag, type: Playbook::Props::Boolean,
18
- default: false
19
-
20
17
  def classname
21
18
  generate_classname("pb_selectable_list_kit")
22
19
  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>
@@ -0,0 +1,3 @@
1
+ This mask feature lets you style your inputs while maintaining the value that the user typed in.
2
+
3
+ There's a hidden input file that stores the raw value, and has the `name` feild. It will also copy the id field with a "#{your-id-sanitized}"
@@ -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