polaris_view_components 0.6.0 → 0.7.0

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -2
  3. data/app/assets/javascripts/polaris_view_components/autocomplete_controller.js +119 -0
  4. data/app/assets/javascripts/polaris_view_components/button_controller.js +4 -5
  5. data/app/assets/javascripts/polaris_view_components/frame_controller.js +41 -0
  6. data/app/assets/javascripts/polaris_view_components/index.js +9 -1
  7. data/app/assets/javascripts/polaris_view_components/option_list_controller.js +41 -0
  8. data/app/assets/javascripts/polaris_view_components/polaris_controller.js +4 -0
  9. data/app/assets/javascripts/polaris_view_components/popover_controller.js +6 -2
  10. data/app/assets/javascripts/polaris_view_components/text_field_controller.js +4 -0
  11. data/app/assets/javascripts/polaris_view_components/toast_controller.js +68 -0
  12. data/app/assets/javascripts/polaris_view_components.js +415 -18
  13. data/app/assets/stylesheets/polaris_view_components/custom.css +41 -0
  14. data/app/assets/stylesheets/polaris_view_components.css +25 -0
  15. data/app/components/polaris/autocomplete/action_component.rb +7 -0
  16. data/app/components/polaris/autocomplete/option_component.rb +35 -0
  17. data/app/components/polaris/autocomplete/section_component.html.erb +9 -0
  18. data/app/components/polaris/autocomplete/section_component.rb +12 -0
  19. data/app/components/polaris/autocomplete_component.html.erb +30 -0
  20. data/app/components/polaris/autocomplete_component.rb +58 -0
  21. data/app/components/polaris/base_checkbox.rb +48 -0
  22. data/app/components/polaris/base_radio_button.rb +38 -0
  23. data/app/components/polaris/checkbox_component.html.erb +1 -5
  24. data/app/components/polaris/checkbox_component.rb +15 -8
  25. data/app/components/polaris/choice_list_component.rb +1 -1
  26. data/app/components/polaris/filters_component.html.erb +22 -0
  27. data/app/components/polaris/filters_component.rb +57 -4
  28. data/app/components/polaris/frame/save_bar_component.html.erb +23 -0
  29. data/app/components/polaris/frame/save_bar_component.rb +31 -0
  30. data/app/components/polaris/frame/top_bar_component.html.erb +30 -0
  31. data/app/components/polaris/frame/top_bar_component.rb +18 -0
  32. data/app/components/polaris/frame_component.html.erb +44 -0
  33. data/app/components/polaris/frame_component.rb +33 -0
  34. data/app/components/polaris/logo.rb +13 -0
  35. data/app/components/polaris/navigation/item_component.html.erb +31 -0
  36. data/app/components/polaris/navigation/item_component.rb +85 -0
  37. data/app/components/polaris/navigation/section_component.html.erb +17 -0
  38. data/app/components/polaris/navigation/section_component.rb +64 -0
  39. data/app/components/polaris/navigation_component.html.erb +29 -0
  40. data/app/components/polaris/navigation_component.rb +15 -0
  41. data/app/components/polaris/option_list/checkbox_component.html.erb +14 -0
  42. data/app/components/polaris/option_list/checkbox_component.rb +37 -0
  43. data/app/components/polaris/option_list/option_component.rb +24 -0
  44. data/app/components/polaris/option_list/radio_button_component.rb +54 -0
  45. data/app/components/polaris/option_list/section_component.html.erb +14 -0
  46. data/app/components/polaris/option_list/section_component.rb +53 -0
  47. data/app/components/polaris/option_list_component.html.erb +15 -0
  48. data/app/components/polaris/option_list_component.rb +67 -0
  49. data/app/components/polaris/popover_component.html.erb +2 -9
  50. data/app/components/polaris/popover_component.rb +17 -0
  51. data/app/components/polaris/radio_button_component.html.erb +1 -6
  52. data/app/components/polaris/radio_button_component.rb +14 -4
  53. data/app/components/polaris/text_field_component.rb +16 -2
  54. data/app/components/polaris/toast_component.html.erb +21 -0
  55. data/app/components/polaris/toast_component.rb +40 -0
  56. data/app/components/polaris/top_bar/user_menu_component.html.erb +19 -0
  57. data/app/components/polaris/top_bar/user_menu_component.rb +9 -0
  58. data/app/helpers/polaris/form_builder.rb +2 -2
  59. data/app/helpers/polaris/view_helper.rb +11 -0
  60. data/lib/polaris/view_components/engine.rb +5 -1
  61. data/lib/polaris/view_components/version.rb +1 -1
  62. metadata +46 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ba771806fadd6b419c7ab4ca160d21199a4634e34c1c6382aeeac8d1908d728
4
- data.tar.gz: 65a6782283ea638ed596e0f75e6125feb2efff9dbdd0169428bbcdb522797ebf
3
+ metadata.gz: b9e5671bf20a8c0aec20920be9fcea01ba39b902057eec479f30b9d621bdbf50
4
+ data.tar.gz: 21bb979fd293cd8379e0e012e9ef2ee9ae06cf62bfdd226fc1ae90ff3b65e9c3
5
5
  SHA512:
6
- metadata.gz: d77337905ac8bfc591a5957546de20b5ff89478d2dca37be41eeddfc1e8053e9623ba452d96c6b8198e0ab185d58b416553319db14d44ad74f30532664c5c4d2
7
- data.tar.gz: 7c69e38fc42ebf27c6f121c9a741b21414c026210e27187aa55cfecc11a7f9cdd60ce511a954e4e2fd94a34b71519104de80c217cff952fe0ce5f802bbf1dfc0
6
+ metadata.gz: 638f5ef1f9c7be3d41b1f1b9016f3a8f14f13c3fbc65c5df6ab8f02b7e903f7456a01514f1d5297c3ecbc022bf66d4283a014a602a9a49662b9d99a368d1a88a
7
+ data.tar.gz: f482994119dd2a2334bf633eae54cb8998d39d38821efc9d16bd88af92869f7f9e18f418cc1f71e2fdf7bbb0d6558cd449c24ae61f93dd660950104b192f4427
data/README.md CHANGED
@@ -8,7 +8,7 @@ Polaris ViewComponents is an implementation of the Polaris Design System using [
8
8
 
9
9
  ## Preview
10
10
 
11
- https://polaris-view-components.herokuapp.com/lookbook
11
+ https://polarisviewcomponents.org
12
12
 
13
13
  ## Usage
14
14
 
@@ -47,6 +47,16 @@ Define Polaris style on your `<body>` tag:
47
47
 
48
48
  ### Importmaps
49
49
 
50
+ Install dependencies:
51
+ ```
52
+ bin/importmap pin @rails/request.js --download
53
+ ```
54
+
55
+ If you use sprockets make sure the vendor folder is loaded in `app/assets/config/manifest.js`:
56
+ ```js
57
+ //= link_tree ../../../vendor/assets/javascripts .js
58
+ ```
59
+
50
60
  Add to `config/importmap.rb`:
51
61
 
52
62
  ```rb
@@ -65,7 +75,7 @@ registerPolarisControllers(Stimulus)
65
75
 
66
76
  Install NPM package:
67
77
  ```bash
68
- yarn add polaris-view-components
78
+ yarn add polaris-view-components @rails/request.js
69
79
  ```
70
80
 
71
81
  Add to `app/javascript/controllers/index.js`:
@@ -0,0 +1,119 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { get } from '@rails/request.js'
3
+
4
+ export default class extends Controller {
5
+ static targets = ['popover', 'input', 'results', 'option', 'emptyState']
6
+ static values = { url: String }
7
+
8
+ connect() {
9
+ this.inputTarget.addEventListener("input", this.onInputChange)
10
+ }
11
+
12
+ disconnect() {
13
+ this.inputTarget.removeEventListener("input", this.onInputChange)
14
+ }
15
+
16
+ // Actions
17
+
18
+ toggle() {
19
+ if (this.visibleOptions.length > 0) {
20
+ this.hideEmptyState()
21
+ this.popoverController.show()
22
+ } else if (this.value.length > 0 && this.hasEmptyStateTarget) {
23
+ this.showEmptyState()
24
+ } else {
25
+ this.popoverController.forceHide()
26
+ }
27
+ }
28
+
29
+ select(event) {
30
+ const input = event.currentTarget
31
+ const label = input.closest('li').dataset.label
32
+ const changeEvent = new CustomEvent('polaris-autocomplete:change', {
33
+ detail: { value: input.value, label, selected: input.checked }
34
+ })
35
+
36
+ this.element.dispatchEvent(changeEvent)
37
+ }
38
+
39
+ onInputChange = debounce(() => {
40
+ if (this.isRemote) {
41
+ this.fetchResults()
42
+ } else {
43
+ this.filterOptions()
44
+ }
45
+ }, 200)
46
+
47
+
48
+ // Private
49
+
50
+ get isRemote() {
51
+ return this.urlValue.length > 0
52
+ }
53
+
54
+ get popoverController() {
55
+ return this.application.getControllerForElementAndIdentifier(this.popoverTarget, 'polaris-popover')
56
+ }
57
+
58
+ get value() {
59
+ return this.inputTarget.value
60
+ }
61
+
62
+ get visibleOptions() {
63
+ return this.optionTargets.filter(option => {
64
+ return !option.classList.contains('Polaris--hidden')
65
+ })
66
+ }
67
+
68
+ filterOptions() {
69
+ if (this.value === '') {
70
+ this.optionTargets.forEach(option => {
71
+ option.classList.remove('Polaris--hidden')
72
+ })
73
+ } else {
74
+ const filterRegex = new RegExp(this.value, 'i')
75
+ this.optionTargets.forEach(option => {
76
+ if (option.dataset.label.match(filterRegex)) {
77
+ option.classList.remove('Polaris--hidden')
78
+ } else {
79
+ option.classList.add('Polaris--hidden')
80
+ }
81
+ })
82
+ }
83
+ this.toggle()
84
+ }
85
+
86
+ async fetchResults() {
87
+ const response = await get(this.urlValue, {
88
+ query: { q: this.value }
89
+ })
90
+ if (response.ok) {
91
+ const results = await response.html
92
+ this.resultsTarget.innerHTML = results
93
+ this.toggle()
94
+ }
95
+ }
96
+
97
+ showEmptyState() {
98
+ if (this.hasEmptyStateTarget) {
99
+ this.resultsTarget.classList.add('Polaris--hidden')
100
+ this.emptyStateTarget.classList.remove('Polaris--hidden')
101
+ }
102
+ }
103
+
104
+ hideEmptyState() {
105
+ if (this.hasEmptyStateTarget) {
106
+ this.emptyStateTarget.classList.add('Polaris--hidden')
107
+ this.resultsTarget.classList.remove('Polaris--hidden')
108
+ }
109
+ }
110
+ }
111
+
112
+ const debounce = (fn, delay = 10) => {
113
+ let timeoutId = null
114
+
115
+ return (...args) => {
116
+ clearTimeout(timeoutId)
117
+ timeoutId = setTimeout(fn, delay)
118
+ }
119
+ }
@@ -2,21 +2,20 @@ import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
4
  disable(event) {
5
- if (this.button.dataset.disabled) {
5
+ if (this.button.disabled) {
6
6
  event.preventDefault()
7
7
  } else {
8
- this.button.dataset.disabled = true
8
+ this.button.disabled = true
9
9
  this.button.classList.add("Polaris-Button--disabled", "Polaris-Button--loading")
10
10
  this.buttonContent.insertAdjacentHTML("afterbegin", this.spinnerHTML)
11
11
  }
12
12
  }
13
13
 
14
14
  enable() {
15
- if (this.button.dataset.disabled) {
15
+ if (this.button.disabled) {
16
16
  this.button.disabled = false
17
- delete this.button.dataset.disabled
18
17
  this.button.classList.remove("Polaris-Button--disabled", "Polaris-Button--loading")
19
- this.spinner.remove()
18
+ if (this.spinner) this.spinner.remove()
20
19
  }
21
20
  }
22
21
 
@@ -0,0 +1,41 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { useTransition } from "stimulus-use"
3
+
4
+ export default class extends Controller {
5
+ static targets = ["navigationOverlay", "navigation", "saveBar"]
6
+
7
+ connect() {
8
+ if (!this.hasNavigationTarget) { return }
9
+
10
+ useTransition(this, {
11
+ element: this.navigationTarget,
12
+ enterFrom: "Polaris-Frame__Navigation--enter",
13
+ enterTo: "Polaris-Frame__Navigation--visible Polaris-Frame__Navigation--enterActive",
14
+ leaveActive: "Polaris-Frame__Navigation--exitActive",
15
+ leaveFrom: "Polaris-Frame__Navigation--exit",
16
+ leaveTo: "",
17
+ removeToClasses: false,
18
+ hiddenClass: false,
19
+ })
20
+ }
21
+
22
+ // Actions
23
+
24
+ openMenu() {
25
+ this.enter()
26
+ this.navigationOverlayTarget.classList.add("Polaris-Backdrop", "Polaris-Backdrop--belowNavigation")
27
+ }
28
+
29
+ closeMenu() {
30
+ this.leave()
31
+ this.navigationOverlayTarget.classList.remove("Polaris-Backdrop", "Polaris-Backdrop--belowNavigation")
32
+ }
33
+
34
+ showSaveBar() {
35
+ this.saveBarTarget.classList.add("Polaris-Frame-CSSAnimation--endFade")
36
+ }
37
+
38
+ hideSaveBar() {
39
+ this.saveBarTarget.classList.remove("Polaris-Frame-CSSAnimation--endFade")
40
+ }
41
+ }
@@ -1,21 +1,29 @@
1
+ import Autocomplete from './autocomplete_controller'
1
2
  import Button from './button_controller'
3
+ import Frame from './frame_controller'
2
4
  import Modal from './modal_controller'
5
+ import OptionList from './option_list_controller'
3
6
  import Polaris from './polaris_controller'
4
7
  import Popover from './popover_controller'
5
8
  import ResourceItem from './resource_item_controller'
6
9
  import Scrollable from './scrollable_controller'
7
10
  import Select from './select_controller'
8
11
  import TextField from './text_field_controller'
12
+ import Toast from './toast_controller'
9
13
 
10
- export { Modal, Polaris, Popover, ResourceItem, Scrollable, Select, TextField }
14
+ export { Frame, Modal, Polaris, Popover, ResourceItem, Scrollable, Select, TextField }
11
15
 
12
16
  export function registerPolarisControllers(application) {
17
+ application.register('polaris-autocomplete', Autocomplete)
13
18
  application.register('polaris-button', Button)
19
+ application.register('polaris-frame', Frame)
14
20
  application.register('polaris-modal', Modal)
21
+ application.register('polaris-option-list', OptionList)
15
22
  application.register('polaris', Polaris)
16
23
  application.register('polaris-popover', Popover)
17
24
  application.register('polaris-resource-item', ResourceItem)
18
25
  application.register('polaris-scrollable', Scrollable)
19
26
  application.register('polaris-select', Select)
20
27
  application.register('polaris-text-field', TextField)
28
+ application.register('polaris-toast', Toast)
21
29
  }
@@ -0,0 +1,41 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["radioButton"]
5
+ static classes = ["selected"]
6
+
7
+ connect() {
8
+ this.updateSelected()
9
+ }
10
+
11
+ // Actions
12
+
13
+ update(event) {
14
+ const target = event.currentTarget
15
+ target.classList.add(this.selectedClass)
16
+ this.deselectAll(target)
17
+ }
18
+
19
+ // Private
20
+
21
+ updateSelected() {
22
+ this.radioButtonTargets.forEach(element => {
23
+ const input = element.querySelector('input[type=radio]')
24
+ if (input.checked) {
25
+ element.classList.add(this.selectedClass)
26
+ } else {
27
+ element.classList.remove(this.selectedClass)
28
+ }
29
+ })
30
+ }
31
+
32
+ deselectAll(target) {
33
+ this.radioButtonTargets.forEach(element => {
34
+ if (!element.isEqualNode(target)) {
35
+ const input = element.querySelector('input[type=radio]')
36
+ input.checked = false
37
+ element.classList.remove(this.selectedClass)
38
+ }
39
+ })
40
+ }
41
+ }
@@ -13,6 +13,10 @@ export default class extends Controller {
13
13
  this.findElement("button").enable()
14
14
  }
15
15
 
16
+ showToast() {
17
+ this.findElement("toast").show()
18
+ }
19
+
16
20
  // Private
17
21
 
18
22
  findElement(type) {
@@ -38,8 +38,12 @@ export default class extends Controller {
38
38
 
39
39
  hide(event) {
40
40
  if (!this.element.contains(event.target) && !this.popoverTarget.classList.contains(this.closedClass)) {
41
- this.popoverTarget.classList.remove(this.openClass)
42
- this.popoverTarget.classList.add(this.closedClass)
41
+ this.forceHide()
43
42
  }
44
43
  }
44
+
45
+ forceHide() {
46
+ this.popoverTarget.classList.remove(this.openClass)
47
+ this.popoverTarget.classList.add(this.closedClass)
48
+ }
45
49
  }
@@ -101,11 +101,15 @@ export default class extends Controller {
101
101
  // step / value has.
102
102
  const decimalPlaces = Math.max(dpl(numericValue), dpl(this.stepValue))
103
103
 
104
+ const oldValue = this.value
104
105
  const newValue = Math.min(
105
106
  Number(this.maxValue),
106
107
  Math.max(numericValue + steps * this.stepValue, Number(this.minValue)),
107
108
  )
108
109
 
109
110
  this.value = String(newValue.toFixed(decimalPlaces))
111
+ if (this.value != oldValue) {
112
+ this.inputTarget.dispatchEvent(new Event('change'))
113
+ }
110
114
  }
111
115
  }
@@ -0,0 +1,68 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static activeClass = 'Polaris-Frame-ToastManager--toastWrapperEnterDone'
5
+ static defaultDuration = 5000
6
+ static defaultDurationWithAction = 10000
7
+ static values = { hidden: Boolean, duration: Number, hasAction: Boolean }
8
+
9
+ connect() {
10
+ if (!this.hiddenValue) {
11
+ this.show()
12
+ }
13
+ }
14
+
15
+ show = () => {
16
+ this.element.dataset.position = this.position
17
+ this.element.style.cssText = this.getStyle(this.position)
18
+ this.element.classList.add(this.constructor.activeClass)
19
+ setTimeout(this.close, this.timeoutDuration)
20
+ }
21
+
22
+ close = () => {
23
+ this.element.classList.remove(this.constructor.activeClass)
24
+ this.element.addEventListener('transitionend', this.updatePositions, false)
25
+ }
26
+
27
+ updatePositions = () => {
28
+ this.visibleToasts
29
+ .sort((a, b) => parseInt(a.dataset.position) - parseInt(b.dataset.position))
30
+ .forEach((toast, index) => {
31
+ const position = index + 1
32
+ toast.dataset.position = position
33
+ toast.style.cssText = this.getStyle(position)
34
+ })
35
+
36
+ this.element.removeEventListener('transitionend', this.updatePositions, false)
37
+ }
38
+
39
+ getStyle(position) {
40
+ const translateIn = -80 * position
41
+ const translateOut = 150 - (80 * position)
42
+ return `--toast-translate-y-in: ${translateIn}px; --toast-translate-y-out: ${translateOut}px;`
43
+ }
44
+
45
+ get timeoutDuration() {
46
+ if (this.durationValue > 0) {
47
+ return this.durationValue
48
+ } else if (this.hasActionValue) {
49
+ return this.constructor.defaultDurationWithAction
50
+ } else {
51
+ return this.constructor.defaultDuration
52
+ }
53
+ }
54
+
55
+ get toastManager() {
56
+ return this.element.closest('.Polaris-Frame-ToastManager')
57
+ }
58
+
59
+ get visibleToasts() {
60
+ return [
61
+ ...this.toastManager.querySelectorAll(`.${this.constructor.activeClass}`)
62
+ ]
63
+ }
64
+
65
+ get position() {
66
+ return this.visibleToasts.filter(el => !this.element.isEqualNode(el)).length + 1
67
+ }
68
+ }