playbook_ui 14.7.0.pre.rc.19 → 14.8.0.pre.alpha.PLAY1615movenegativetoleftofcurrencysign4539

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_card/docs/_card_light.md +1 -1
  3. data/app/pb_kits/playbook/pb_currency/_currency.tsx +7 -3
  4. data/app/pb_kits/playbook/pb_currency/currency.html.erb +2 -2
  5. data/app/pb_kits/playbook/pb_currency/currency.rb +17 -1
  6. data/app/pb_kits/playbook/pb_date/_date.scss +3 -0
  7. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +1 -1
  8. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_default_date.html.erb +42 -0
  9. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_default_date.jsx +44 -0
  10. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_default_date.md +1 -0
  11. data/app/pb_kits/playbook/pb_date_picker/docs/example.yml +2 -0
  12. data/app/pb_kits/playbook/pb_date_picker/docs/index.js +1 -0
  13. data/app/pb_kits/playbook/pb_date_picker/plugins/quickPick.tsx +17 -1
  14. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_default_rails.html.erb +26 -0
  15. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_default_rails.md +7 -0
  16. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_rails.html.erb +38 -0
  17. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_cards_rails.md +0 -0
  18. data/app/pb_kits/playbook/pb_draggable/docs/_draggable_with_list_rails.html.erb +19 -0
  19. data/app/pb_kits/playbook/pb_draggable/docs/example.yml +6 -0
  20. data/app/pb_kits/playbook/pb_draggable/draggable.html.erb +3 -0
  21. data/app/pb_kits/playbook/pb_draggable/draggable.rb +18 -0
  22. data/app/pb_kits/playbook/pb_draggable/draggable_container.html.erb +3 -0
  23. data/app/pb_kits/playbook/pb_draggable/draggable_container.rb +15 -0
  24. data/app/pb_kits/playbook/pb_draggable/draggable_item.html.erb +7 -0
  25. data/app/pb_kits/playbook/pb_draggable/draggable_item.rb +18 -0
  26. data/app/pb_kits/playbook/pb_draggable/index.js +125 -0
  27. data/app/pb_kits/playbook/pb_drawer/_drawer.scss +88 -175
  28. data/app/pb_kits/playbook/pb_drawer/_drawer.tsx +79 -47
  29. data/app/pb_kits/playbook/pb_drawer/drawer.test.jsx +20 -16
  30. data/app/pb_kits/playbook/pb_timeline/_timeline.scss +43 -1
  31. data/app/pb_kits/playbook/pb_timeline/_timeline.tsx +6 -2
  32. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.html.erb +94 -0
  33. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.jsx +180 -0
  34. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_gap.md +1 -0
  35. data/app/pb_kits/playbook/pb_timeline/docs/example.yml +5 -3
  36. data/app/pb_kits/playbook/pb_timeline/docs/index.js +2 -0
  37. data/app/pb_kits/playbook/pb_timeline/timeline.rb +11 -1
  38. data/app/pb_kits/playbook/pb_timeline/timeline.test.js +4 -4
  39. data/app/pb_kits/playbook/utilities/_hover.scss +40 -27
  40. data/app/pb_kits/playbook/utilities/globalProps.ts +5 -3
  41. data/app/pb_kits/playbook/utilities/test/globalProps/hover.test.js +79 -0
  42. data/dist/chunks/_typeahead-D0PihN_3.js +22 -0
  43. data/dist/chunks/_weekday_stacked-CVx1CzK-.js +45 -0
  44. data/dist/chunks/{lib-D-mTv-kp.js → lib-BC6ESsxG.js} +1 -1
  45. data/dist/chunks/{pb_form_validation-BkWGwJsl.js → pb_form_validation-B_Z9rEbg.js} +1 -1
  46. data/dist/chunks/vendor.js +1 -1
  47. data/dist/menu.yml +1 -1
  48. data/dist/playbook-doc.js +1 -1
  49. data/dist/playbook-rails-react-bindings.js +1 -1
  50. data/dist/playbook-rails.js +1 -1
  51. data/dist/playbook.css +1 -1
  52. data/lib/playbook/hover.rb +1 -1
  53. data/lib/playbook/version.rb +2 -2
  54. metadata +25 -6
  55. data/dist/chunks/_typeahead-DhLic2Fe.js +0 -22
  56. data/dist/chunks/_weekday_stacked-Mx8TYP5I.js +0 -45
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60f5cff6912e2865399cef73254a7b2dc0c44d6569424298bb634f68f32379cd
4
- data.tar.gz: 83369d0166a9aa85827c3df19ed00880ad84d6b258c03dc2ff48f2de41c05c75
3
+ metadata.gz: fd8c9650b23708306eeea2d36f0a87f8b065d8aa28fbd72bfef581827414c6da
4
+ data.tar.gz: e941e09a241073a2641e0a42b5e3b42e67db2cc6ea4fdf903d54bf8986b3e13c
5
5
  SHA512:
6
- metadata.gz: 9d9fc03ccd849622b9f5cc1bc47deb85a9733c42fbc986ebe01c3bdcc7ce7446a70f2c21f9da2da2bb3335ba826da717f76b1ccc8a9f5ef8e7aecd2414cb4bf4
7
- data.tar.gz: 3f2825d50037ea12633a8ad29a02769e85796681ed3fbaa63b846384e8c50a99fafdaa78eb7bdaea4c20a88379d165d9b94d3f251458f2ab2c8b48c18ce9a2f2
6
+ metadata.gz: 2ffec4416dafad8b69973e7f96276279ecdfc2aa2a9ada8c002a188fe99061db0b22a2a8a5b662a661bab1562b961b3c3db8f8924959b859383c1e9d17e4483b
7
+ data.tar.gz: d2b75f2d74f6ddb913b171976b07a372cbfa539524e06f36ece2f82dd377e66087290830c64735ddf9c158ded7fdb97aaab9fb6c6f1932df364104ef0a661b3e
@@ -1 +1 @@
1
- Card can leverage the max-width property. Learn more in our <a href="https://playbook.powerapp.cloud/visual_guidelines" target="_blank">visual guidelines.</a>
1
+ Card can leverage the max-width property. Learn more in our <a href="https://playbook.powerapp.cloud/visual_guidelines" target="_blank">visual guidelines.</a>
@@ -101,7 +101,11 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
101
101
  return decimalPart ? `${formattedWhole}.${decimalPart}` : formattedWhole;
102
102
  }
103
103
 
104
- const getAmount = abbreviate ? getAbbreviatedValue('amount') : formatAmount(getMatchingDecimalAmount)
104
+ const swapNegative = size === "sm" && symbol !== ""
105
+ const handleNegative = amount.startsWith("-") && swapNegative ? "-" : ""
106
+ const getAbsoluteAmount = (amountString) => amountString.replace(/^-/,'')
107
+ const getAbbrOrFormatAmount = abbreviate ? getAbbreviatedValue('amount') : formatAmount(getMatchingDecimalAmount)
108
+ const getAmount = swapNegative ? getAbsoluteAmount(getAbbrOrFormatAmount) : getAbbrOrFormatAmount
105
109
  const getAbbreviation = abbreviate ? getAbbreviatedValue('unit') : null
106
110
  const getDecimalValue = abbreviate ? '' : getMatchingDecimalValue
107
111
 
@@ -118,7 +122,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
118
122
  <div className={`pb_currency_wrapper${variantClass || emphasizedClass}`}>
119
123
  {unstyled ? (
120
124
  <>
121
- <div>{symbol}</div>
125
+ <div>{handleNegative}{symbol}</div>
122
126
  <div>{getAmount}</div>
123
127
  <div>
124
128
  {getAbbreviation}
@@ -132,7 +136,7 @@ const Currency = (props: CurrencyProps): React.ReactElement => {
132
136
  color="light"
133
137
  dark={dark}
134
138
  >
135
- {symbol}
139
+ {handleNegative}{symbol}
136
140
  </Body>
137
141
 
138
142
  <Title
@@ -3,12 +3,12 @@
3
3
 
4
4
  <div class=<%= "pb_currency_wrapper#{object.variant_class || object.emphasized_class}" %>>
5
5
  <% if object.unstyled %>
6
- <div><%= object.symbol %></div>
6
+ <div><%= object.negative_sign %><%= object.symbol %></div>
7
7
  <div><%= object.title_props[:text] %></div>
8
8
  <div><%= object.body_props[:text] %></div>
9
9
  <% else %>
10
10
  <%= pb_rails("body", props: object.currency_wrapper_props) do %>
11
- <%= object.symbol %>
11
+ <%= object.negative_sign %><%= object.symbol %>
12
12
  <% end %>
13
13
  <%= pb_rails("title", props: object.title_props) %>
14
14
  <%= pb_rails("body", props: object.body_props) %>
@@ -68,12 +68,20 @@ module Playbook
68
68
  def title_props
69
69
  {
70
70
  size: size_value,
71
- text: abbreviate ? abbreviated_value : formatted_amount,
71
+ text: swap_negative ? absolute_amount(abbr_or_format_amount) : abbr_or_format_amount,
72
72
  classname: "pb_currency_value",
73
73
  dark: dark,
74
74
  }
75
75
  end
76
76
 
77
+ def abbr_or_format_amount
78
+ abbreviate ? abbreviated_value : formatted_amount
79
+ end
80
+
81
+ def negative_sign
82
+ amount.starts_with?("-") && swap_negative ? "-" : ""
83
+ end
84
+
77
85
  def body_props
78
86
  {
79
87
  text: units_element,
@@ -159,6 +167,14 @@ module Playbook
159
167
  whole_value
160
168
  end
161
169
  end
170
+
171
+ def absolute_amount(amount_string)
172
+ amount_string.sub(/^-/, "")
173
+ end
174
+
175
+ def swap_negative
176
+ size == "sm" && symbol != ""
177
+ end
162
178
  end
163
179
  end
164
180
  end
@@ -28,5 +28,8 @@
28
28
  [class^=pb_title_kit] {
29
29
  color: $text_dk_default !important;
30
30
  }
31
+ [class^=pb_body_kit], [class^=pb_caption_kit] {
32
+ color: $text_dk_light !important;
33
+ }
31
34
  }
32
35
  }
@@ -143,7 +143,7 @@ const datePickerHelper = (config: DatePickerConfig, scrollContainer: string | HT
143
143
 
144
144
  } else if (selectionType === "quickpick") {
145
145
  //------- QUICKPICK VARIANT PLUGIN -------------//
146
- pluginList.push(quickPickPlugin(thisRangesEndToday, customQuickPickDates))
146
+ pluginList.push(quickPickPlugin(thisRangesEndToday, customQuickPickDates, defaultDate as string))
147
147
  }
148
148
 
149
149
  // time selection
@@ -0,0 +1,42 @@
1
+ <%= pb_rails("date_picker", props: {
2
+ allow_input: true,
3
+ default_date: "This month",
4
+ end_date_id: "quick-pick-end-date",
5
+ end_date_name: "quick-pick-end-date",
6
+ mode: "range",
7
+ picker_id: "quick-pick-default-date",
8
+ placeholder: "mm/dd/yyyy to mm/dd/yyyy",
9
+ selection_type: "quickpick",
10
+ start_date_id: "quick-pick-start-date",
11
+ start_date_name: "quick-pick-start-date"
12
+ }) %>
13
+
14
+ <%= pb_rails("date_picker", props: {
15
+ allow_input: true,
16
+ custom_quick_pick_dates: {
17
+ dates: [
18
+ {
19
+ label: "Last 15 months",
20
+ value: {
21
+ timePeriod: "months",
22
+ amount: 15,
23
+ },
24
+ },
25
+ {
26
+ label: "First Week of June 2022",
27
+ value: ["06/01/2022", "06/07/2022"],
28
+ },
29
+ ],
30
+ },
31
+ default_date: "First Week of June 2022",
32
+ end_date_id: "quick-pick-end-date",
33
+ end_date_name: "quick-pick-end-date",
34
+ label: "Custom Date Picker",
35
+ mode: "range",
36
+ picker_id: "custom-quick-pick-default-date",
37
+ placeholder: "mm/dd/yyyy to mm/dd/yyyy",
38
+ selection_type: "quickpick",
39
+ start_date_id: "quick-pick-start-date",
40
+ start_date_name: "quick-pick-start-date"
41
+ }) %>
42
+
@@ -0,0 +1,44 @@
1
+ import React from "react"
2
+ import DatePicker from "../_date_picker"
3
+
4
+ const DatePickerQuickPickDefaultDate = (props) => (
5
+ <>
6
+ <DatePicker
7
+ allowInput
8
+ defaultDate="This month"
9
+ mode="range"
10
+ pickerId="quick-pick-default-date"
11
+ placeholder="mm/dd/yyyy to mm/dd/yyyy"
12
+ selectionType="quickpick"
13
+ {...props}
14
+ />
15
+
16
+ <DatePicker
17
+ allowInput
18
+ customQuickPickDates={{
19
+ dates: [
20
+ {
21
+ label: "Last 15 months",
22
+ value: {
23
+ timePeriod: "months",
24
+ amount: 15,
25
+ },
26
+ },
27
+ {
28
+ label: "First Week of June 2022",
29
+ value: ["06/01/2022", "06/07/2022"],
30
+ },
31
+ ],
32
+ }}
33
+ defaultDate="First Week of June 2022"
34
+ label="Custom Date Picker"
35
+ mode="range"
36
+ pickerId="custom-quick-pick-default-date"
37
+ placeholder="mm/dd/yyyy to mm/dd/yyyy"
38
+ selectionType="quickpick"
39
+ {...props}
40
+ />
41
+ </>
42
+ )
43
+
44
+ export default DatePickerQuickPickDefaultDate
@@ -0,0 +1 @@
1
+ To set a default value using Quick Pick, use the `defaultDate` or `default_date` prop. This prop should match one of the labels displayed in the UI of the dropdown menu.
@@ -12,6 +12,7 @@ examples:
12
12
  - date_picker_quick_pick_range_limit: Range (Quick Pick w/ “This” Range limit)
13
13
  - date_picker_quick_pick_custom: Custom Quick Pick Dates
14
14
  - date_picker_quick_pick_custom_override: Custom Quick Pick Dates (append to defaults)
15
+ - date_picker_quick_pick_default_date: Range (Quick Pick w/ Default Date)
15
16
  - date_picker_format: Format
16
17
  - date_picker_disabled: Disabled Dates
17
18
  - date_picker_min_max: Min Max
@@ -42,6 +43,7 @@ examples:
42
43
  - date_picker_quick_pick_range_limit: Range (Quick Pick w/ “This” Range limit)
43
44
  - date_picker_quick_pick_custom: Custom Quick Pick Dates
44
45
  - date_picker_quick_pick_custom_override: Custom Quick Pick Dates (append to defaults)
46
+ - date_picker_quick_pick_default_date: Range (Quick Pick w/ Default Date)
45
47
  - date_picker_format: Format
46
48
  - date_picker_disabled: Disabled Dates
47
49
  - date_picker_min_max: Min Max
@@ -25,3 +25,4 @@ export { default as DatePickerQuickPickRangeLimit } from './_date_picker_quick_p
25
25
  export { default as DatePickerOnClose } from './_date_picker_on_close.jsx'
26
26
  export { default as DatePickerQuickPickCustom } from './_date_picker_quick_pick_custom'
27
27
  export { default as DatePickerQuickPickCustomOverride } from './_date_picker_quick_pick_custom_override'
28
+ export { default as DatePickerQuickPickDefaultDate } from './_date_picker_quick_pick_default_date'
@@ -26,7 +26,7 @@ type customQuickPickDatesType = {
26
26
 
27
27
  let activeLabel = ""
28
28
 
29
- const quickPickPlugin = (thisRangesEndToday: boolean, customQuickPickDates: customQuickPickDatesType | undefined) => {
29
+ const quickPickPlugin = (thisRangesEndToday: boolean, customQuickPickDates: customQuickPickDatesType | undefined, defaultDate: string) => {
30
30
  return function (fp: FpTypes & any): any {
31
31
  const today = new Date()
32
32
  const yesterday = DateTime.getYesterdayDate(new Date())
@@ -185,6 +185,8 @@ const quickPickPlugin = (thisRangesEndToday: boolean, customQuickPickDates: cust
185
185
  return {
186
186
  // onReady is a hook from flatpickr that runs when calendar is in a ready state
187
187
  onReady(selectedDates: Array<Date>) {
188
+ let defaultDateRange
189
+
188
190
  // loop through the ranges and create an anchor tag for each range and add an event listener to set the date when user clicks on a date range
189
191
  for (const [label, range] of Object.entries(pluginData.ranges)) {
190
192
  addRangeButton(label).addEventListener('click', function () {
@@ -201,6 +203,14 @@ const quickPickPlugin = (thisRangesEndToday: boolean, customQuickPickDates: cust
201
203
  fp.close();
202
204
  }
203
205
  });
206
+
207
+ // check if there is a default date and set the default date range and label for quick pick
208
+ if (defaultDate) {
209
+ if (label.toLowerCase() === defaultDate.toLowerCase()) {
210
+ activeLabel = label
211
+ defaultDateRange = range
212
+ }
213
+ }
204
214
  }
205
215
  // conditional to check if there is a dropdown to add it to the calendar container and get it the classes it needs
206
216
  if (pluginData.rangesNav.children.length > 0) {
@@ -216,6 +226,12 @@ const quickPickPlugin = (thisRangesEndToday: boolean, customQuickPickDates: cust
216
226
  // function to give the active button the active class
217
227
  selectActiveRangeButton(selectedDates);
218
228
  }
229
+
230
+ // set the default date range if there is one and select the active button
231
+ if (defaultDateRange) {
232
+ fp.setDate(defaultDateRange, false);
233
+ selectActiveRangeButton(defaultDateRange);
234
+ }
219
235
  },
220
236
  onValueUpdate(selectedDates: Array<Date>) {
221
237
  selectActiveRangeButton(selectedDates)
@@ -0,0 +1,26 @@
1
+ <% initial_items = [
2
+ {
3
+ id: "1",
4
+ url: "https://unsplash.it/500/400/?image=633",
5
+ },
6
+ {
7
+ id: "2",
8
+ url: "https://unsplash.it/500/400/?image=634",
9
+ },
10
+ {
11
+ id: "3",
12
+ url: "https://unsplash.it/500/400/?image=637",
13
+ },
14
+ ] %>
15
+
16
+ <%= pb_rails("draggable", props: {initial_items: initial_items}) do %>
17
+ <%= pb_rails("draggable/draggable_container") do %>
18
+ <%= pb_rails("flex") do %>
19
+ <% initial_items.each do |item| %>
20
+ <%= pb_rails("draggable/draggable_item", props:{drag_id: item[:id]}) do %>
21
+ <%= pb_rails("image", props: { alt: item[:id], size: "md", url: item[:url], margin: "xs" }) %>
22
+ <% end %>
23
+ <% end %>
24
+ <% end %>
25
+ <% end %>
26
+ <% end %>
@@ -0,0 +1,7 @@
1
+ The `draggable` kit gives you a full subcomponent structure that allows it to be used with almost any kit.
2
+
3
+ `initial_items` is a REQUIRED prop, which is the array of objects that contains data for the the draggable items.
4
+
5
+ `draggable/draggable_container` = This specifies the container within which items can be dropped.
6
+
7
+ `draggable/draggable_item` = This specifies the items that can be dragged and dropped. `drag_id` is a REQUIRED prop for draggable_item and must match the id on the items within `initial_items`.
@@ -0,0 +1,38 @@
1
+ <% initial_items = [
2
+ { id: "21", name: "Joe Black" },
3
+ { id: "22", name: "Nancy White" },
4
+ { id: "23", name: "Bill Green" },
5
+ ] %>
6
+
7
+ <%= pb_rails("draggable", props: {initial_items: initial_items}) do %>
8
+ <%= pb_rails("draggable/draggable_container") do %>
9
+ <% initial_items.each do |item| %>
10
+ <%= pb_rails("draggable/draggable_item", props:{drag_id: item[:id]}) do %>
11
+ <%= pb_rails("card", props: {highlight: {position: "side", color:"primary"}, margin_bottom: "xs", padding: "xs"}) do %>
12
+ <%= pb_rails("flex", props:{align_items: "stretch", flex_direction:"column"}) do %>
13
+ <%= pb_rails("flex", props:{gap: "xs"}) do %>
14
+ <%= pb_rails("title", props: { text: item[:name], tag: "h4", size: 4 }) %>
15
+ <%= pb_rails("badge", props: {text:"35-12345" ,variant: "primary"}) %>
16
+ <% end %>
17
+ <%= pb_rails("caption", props: { size: "xs", text: "8:00A • Township Name • 90210" }) %>
18
+ <%= pb_rails("flex", props:{gap: "xxs", spacing:"between"}) do %>
19
+ <%= pb_rails("flex", props:{gap: "xxs"}) do %>
20
+ <%= pb_rails("caption", props: { size: "xs" , color: "error" }) do %>
21
+ <%= pb_rails("icon", props: { icon: "house-circle-exclamation", fixed_width: true }) %>
22
+ <% end %>
23
+ <%= pb_rails("caption", props: { size: "xs" , color: "success" }) do %>
24
+ <%= pb_rails("icon", props: { icon: "file-circle-check", fixed_width: true }) %>
25
+ <% end %>
26
+ <% end %>
27
+ <%= pb_rails("flex") do %>
28
+ <%= pb_rails("badge", props: {text:"Schedule QA" ,variant: "warning", rounded: true}) %>
29
+ <%= pb_rails("badge", props: {text:"Flex" ,variant: "primary", rounded: true}) %>
30
+ <%= pb_rails("badge", props: {text:"R99" ,variant: "primary", rounded: true}) %>
31
+ <% end %>
32
+ <% end %>
33
+ <% end %>
34
+ <% end %>
35
+ <% end %>
36
+ <% end %>
37
+ <% end %>
38
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <% initial_items = [
2
+ { id: "31", name: "Philadelphia" },
3
+ { id: "32", name: "New Jersey" },
4
+ { id: "33", name: "Maryland" },
5
+ { id: "34", name: "Connecticut" },
6
+
7
+ ] %>
8
+
9
+ <%= pb_rails("draggable", props: {initial_items: initial_items}) do %>
10
+ <%= pb_rails("draggable/draggable_container") do %>
11
+ <%= pb_rails("list", props: {ordered: false}) do %>
12
+ <% initial_items.each do |item| %>
13
+ <%= pb_rails("draggable/draggable_item", props:{drag_id: item[:id]}) do %>
14
+ <%= pb_rails("list/item") do %><%= item[:name] %><% end %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
18
+ <% end %>
19
+ <% end %>
@@ -8,4 +8,10 @@ examples:
8
8
  - draggable_with_cards: Draggable with Cards
9
9
  - draggable_multiple_containers: Dragging Across Multiple Containers
10
10
 
11
+ rails:
12
+ - draggable_default_rails: Default
13
+ - draggable_with_list_rails: Draggable with List Kit
14
+ - draggable_with_cards_rails: Draggable with Cards
15
+
16
+
11
17
 
@@ -0,0 +1,3 @@
1
+ <%= pb_content_tag do %>
2
+ <%= content.presence %>
3
+ <% end %>
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Playbook
4
+ module PbDraggable
5
+ class Draggable < ::Playbook::KitBase
6
+ prop :initial_items, type: Playbook::Props::Array,
7
+ default: []
8
+
9
+ def data
10
+ Hash(prop(:data)).merge(pb_draggable: true)
11
+ end
12
+
13
+ def classname
14
+ generate_classname("pb_draggable")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ <%= pb_content_tag do %>
2
+ <%= content.presence %>
3
+ <% end %>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Playbook
4
+ module PbDraggable
5
+ class DraggableContainer < ::Playbook::KitBase
6
+ def data
7
+ Hash(prop(:data)).merge(pb_draggable_container: true)
8
+ end
9
+
10
+ def classname
11
+ generate_classname("pb_draggable_container")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ <%= pb_content_tag(:div, {
2
+ id: "item_#{object.drag_id}",
3
+ draggable: true
4
+ }) do %>
5
+ <%= content.presence %>
6
+ <% end %>
7
+
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Playbook
4
+ module PbDraggable
5
+ class DraggableItem < ::Playbook::KitBase
6
+ prop :drag_id, type: Playbook::Props::String,
7
+ default: ""
8
+
9
+ def data
10
+ Hash(prop(:data)).merge(pb_draggable_item: true)
11
+ end
12
+
13
+ def classname
14
+ generate_classname("pb_draggable_item")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,125 @@
1
+ import PbEnhancedElement from "../pb_enhanced_element";
2
+
3
+ const DRAGGABLE_SELECTOR = "[data-pb-draggable]";
4
+ const DRAGGABLE_CONTAINER = ".pb_draggable_container";
5
+
6
+ export default class PbDraggable extends PbEnhancedElement {
7
+ static get selector() {
8
+ return DRAGGABLE_SELECTOR;
9
+ }
10
+
11
+ connect() {
12
+ this.draggedItem = null;
13
+ this.draggedItemId = null;
14
+ document.addEventListener("DOMContentLoaded", () => this.bindEventListeners());
15
+ }
16
+
17
+ bindEventListeners() {
18
+ // Needed to prevent images within draggable items from being independently draggable
19
+ // Needed if using Image kit in draggable items
20
+ this.element.querySelectorAll(".pb_draggable_item img").forEach(img => {
21
+ img.setAttribute("draggable", "false");
22
+ });
23
+
24
+ this.element.querySelectorAll(".pb_draggable_item").forEach(item => {
25
+ item.addEventListener("dragstart", this.handleDragStart.bind(this));
26
+ item.addEventListener("dragend", this.handleDragEnd.bind(this));
27
+ item.addEventListener("dragenter", this.handleDragEnter.bind(this));
28
+ });
29
+
30
+ const container = this.element.querySelector(DRAGGABLE_CONTAINER);
31
+ if (container) {
32
+ container.addEventListener("dragover", this.handleDragOver.bind(this));
33
+ container.addEventListener("drop", this.handleDrop.bind(this));
34
+ }
35
+ }
36
+
37
+ handleDragStart(event) {
38
+ // Needed to prevent images within draggable items from being independently draggable
39
+ // Needed if using Image kit in draggable items
40
+ if (event.target.tagName.toLowerCase() === 'img') {
41
+ event.preventDefault();
42
+ return;
43
+ }
44
+
45
+ this.draggedItem = event.target;
46
+ this.draggedItemId = event.target.id;
47
+ event.target.classList.add("is_dragging");
48
+
49
+ if (event.dataTransfer) {
50
+ event.dataTransfer.effectAllowed = 'move';
51
+ event.dataTransfer.setData('text/plain', this.draggedItemId);
52
+ }
53
+
54
+ setTimeout(() => {
55
+ event.target.style.opacity = '0.5';
56
+ }, 0);
57
+ }
58
+
59
+ handleDragEnter(event) {
60
+ if (!this.draggedItem || event.target === this.draggedItem) return;
61
+
62
+ const targetItem = event.target.closest('.pb_draggable_item');
63
+ if (!targetItem) return;
64
+
65
+ const container = targetItem.parentNode;
66
+ const items = Array.from(container.children);
67
+ const draggedIndex = items.indexOf(this.draggedItem);
68
+ const targetIndex = items.indexOf(targetItem);
69
+
70
+ if (draggedIndex > targetIndex) {
71
+ container.insertBefore(this.draggedItem, targetItem);
72
+ } else {
73
+ container.insertBefore(this.draggedItem, targetItem.nextSibling);
74
+ }
75
+ }
76
+
77
+ handleDragOver(event) {
78
+ event.preventDefault();
79
+ const container = event.target.closest(DRAGGABLE_CONTAINER);
80
+
81
+ if (container) {
82
+ container.classList.add("active_container");
83
+ }
84
+ }
85
+
86
+ handleDrop(event) {
87
+ event.preventDefault();
88
+ const container = event.target.closest(DRAGGABLE_CONTAINER);
89
+ if (!container || !this.draggedItem) return;
90
+
91
+ container.classList.remove("active_container");
92
+ this.draggedItem.style.opacity = '1';
93
+
94
+ // Updated order of items as an array of item IDs
95
+ const reorderedItems = Array.from(container.children)
96
+ .filter(item => item.classList.contains("pb_draggable_item"))
97
+ .map(item => item.id.replace("item_", ""));
98
+
99
+ // Store reordered items in a data attribute on the container
100
+ container.setAttribute("data-reordered-items", JSON.stringify(reorderedItems));
101
+
102
+ const customEvent = new CustomEvent('pb-draggable-reorder', {
103
+ detail: {
104
+ reorderedItems,
105
+ containerId: container.id,
106
+ }
107
+ });
108
+ this.element.dispatchEvent(customEvent);
109
+
110
+ this.draggedItem = null;
111
+ this.draggedItemId = null;
112
+ }
113
+
114
+
115
+ handleDragEnd(event) {
116
+ event.target.classList.remove("is_dragging");
117
+ event.target.style.opacity = '1';
118
+ this.draggedItem = null;
119
+ this.draggedItemId = null;
120
+
121
+ this.element.querySelectorAll(DRAGGABLE_CONTAINER).forEach(container => {
122
+ container.classList.remove("active_container");
123
+ });
124
+ }
125
+ }