polaris_view_components 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }