coveragebook_components 0.17.7 → 0.18.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5535752274648feee015c6471880b40d3ddc2cd63e8836ec3839b7461917e530
4
- data.tar.gz: 34fba281df9eddd0a2f1449bb8b4abcbb1cae0e4fdcee50c9d7db71c814abf8b
3
+ metadata.gz: a6e884ae365d700946ff6b8fcaba45157c23dd63f1c99eb9bfe479d8ffee61c1
4
+ data.tar.gz: 74c0748337f4a09bea38d496e71c7c62f39282fdb457bb8bc443507171f58ba2
5
5
  SHA512:
6
- metadata.gz: c8b43623f1feeffe47afe6b28fd418b0e3be58ff0059eeb20eb522f33617a781a9e140645aaeee5096b678e99aa5abb346e02904b8a12e974c1a7b991f066464
7
- data.tar.gz: 1da47f77ae7ce00536379c4e05368b3c24ef7e1331f2e9f7fbc15f56c63f157b2e337c37ba44df3af140599f22f1e1a1ad1ae189f5ffdced64b9b83cadd4a3d4
6
+ metadata.gz: 7213a41ac90c6eb3288873ca5f82f36a6dd816cb98e865fe9089a7962c8efc5b8052926ac67d709b4c87a2c06af34cb2b8a61af8a07bb2fd2abcbfd70994fb11
7
+ data.tar.gz: 248c6c5d9c9d17a8aef5de3c352cb0148c0574dd516272344fdbb01633afe3673279f1357a66c10d1c74ce46640cfbcaf6297dbddcdcafd206ce0911457535eb
@@ -4161,6 +4161,11 @@ select{
4161
4161
  padding-bottom: 1rem
4162
4162
  }
4163
4163
 
4164
+ [data-coco][data-component="popover"] {
4165
+ display: none;
4166
+ pointer-events: none;
4167
+ }
4168
+
4164
4169
  [data-coco][data-component="snackbar"]{
4165
4170
  width: 100%;
4166
4171
  overflow: hidden;
@@ -5779,10 +5784,6 @@ select{
5779
5784
  position: fixed
5780
5785
  }
5781
5786
 
5782
- .\!block{
5783
- display: block !important
5784
- }
5785
-
5786
5787
  .block{
5787
5788
  display: block
5788
5789
  }
@@ -6963,3 +6964,56 @@ select{
6963
6964
  width: -moz-min-content;
6964
6965
  width: min-content;
6965
6966
  }
6967
+
6968
+ /* Tippy popover theme */
6969
+
6970
+ .tippy-box[data-theme^="coco-popover-"]{
6971
+ border-radius: 0.5rem;
6972
+ border-style: none;
6973
+ padding: 1.5rem;
6974
+ font-size: 16px;
6975
+ line-height: 24px;
6976
+ font-weight: 400;
6977
+ --tw-text-opacity: 1;
6978
+ color: rgb(255 255 255 / var(--tw-text-opacity));
6979
+ --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
6980
+ --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);
6981
+ box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
6982
+ width: -moz-fit-content;
6983
+ width: fit-content
6984
+ }
6985
+
6986
+ .tippy-box[data-theme^="coco-popover-"] .tippy-svg-arrow > svg:first-child{
6987
+ display: none
6988
+ }
6989
+
6990
+ .tippy-box[data-theme~="coco-popover-neutral"]{
6991
+ border-width: 1px;
6992
+ border-style: solid;
6993
+ --tw-bg-opacity: 1;
6994
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
6995
+ --tw-text-opacity: 1;
6996
+ color: rgb(17 24 39 / var(--tw-text-opacity));
6997
+ background-blend-mode: hard-light;
6998
+ border-color: rgba(0, 12, 39, 0.1)
6999
+ }
7000
+
7001
+ .tippy-box[data-theme~="coco-popover-neutral"] .tippy-svg-arrow > svg:first-child{
7002
+ display: block;
7003
+ fill: #D1D5DB
7004
+ }
7005
+
7006
+ .tippy-box[data-theme~="coco-popover-neutral"] .tippy-svg-arrow > svg:last-child{
7007
+ fill: #FFFFFF
7008
+ }
7009
+
7010
+ .tippy-box[data-theme~="coco-popover-info"]{
7011
+ --tw-bg-opacity: 1;
7012
+ background-color: rgb(2 132 199 / var(--tw-bg-opacity));
7013
+ -webkit-font-smoothing: antialiased;
7014
+ -moz-osx-font-smoothing: grayscale
7015
+ }
7016
+
7017
+ .tippy-box[data-theme~="coco-popover-info"] .tippy-svg-arrow > svg:last-child{
7018
+ fill: #0284C7
7019
+ }
@@ -15260,7 +15260,7 @@ function dropdown_default(Alpine3) {
15260
15260
  const settings = tippyModifiers(modifiers);
15261
15261
  const result = expression ? evaluate(expression) : {};
15262
15262
  const directiveConfig = isObject(result) ? result : {};
15263
- let { triggerTarget, contentTarget, anchorTarget, ...config } = directiveConfig;
15263
+ let { triggerTarget, contentTarget, anchorTarget, flip, ...config } = directiveConfig;
15264
15264
  contentTarget = contentTarget || el.querySelector("[x-dropdown\\:content]");
15265
15265
  const content = isNode(contentTarget) ? contentTarget.firstElementChild : contentTarget;
15266
15266
  triggerTarget = triggerTarget || el.querySelector("[x-dropdown\\:trigger]") || el;
@@ -15288,6 +15288,9 @@ function dropdown_default(Alpine3) {
15288
15288
  interactive: true,
15289
15289
  animation: false,
15290
15290
  maxWidth: 380,
15291
+ popperOptions: {
15292
+ modifiers: [{ name: "flip", enabled: flip !== false }]
15293
+ },
15291
15294
  triggerTarget,
15292
15295
  content,
15293
15296
  onShow: (...args) => {
@@ -15441,7 +15444,7 @@ var alpine_default = import_alpinejs.default;
15441
15444
  // ../../../package.json
15442
15445
  var package_default = {
15443
15446
  name: "coveragebook-components",
15444
- version: "0.17.7",
15447
+ version: "0.18.1",
15445
15448
  repository: "git@github.com:coveragebook/coco.git",
15446
15449
  license: "NO LICENSE",
15447
15450
  author: "Mark Perkins <mark@coveragebook.com>",
@@ -16195,6 +16198,112 @@ var notice_default = CocoComponent("notice", () => {
16195
16198
  return {};
16196
16199
  });
16197
16200
 
16201
+ // ../../components/coco/messaging/popover/popover.js
16202
+ var popover_exports = {};
16203
+ __export(popover_exports, {
16204
+ default: () => popover_default
16205
+ });
16206
+ var popover_default = CocoComponent("popover", ({ target, trigger, options = {} }) => {
16207
+ return {
16208
+ targetEl: null,
16209
+ open: false,
16210
+ init() {
16211
+ this.targetEl = target ? document.querySelector(target) : this.$el.parentElement;
16212
+ if (this.targetEl) {
16213
+ this.targetEl.coco_popover = this;
16214
+ if (trigger == "click") {
16215
+ this.targetEl.style.cursor = "pointer";
16216
+ }
16217
+ this.$nextTick(() => {
16218
+ const content = this.$el.firstElementChild;
16219
+ content.__popover_target = this.targetEl;
16220
+ tippy_default(this.targetEl, {
16221
+ trigger,
16222
+ theme: this.theme,
16223
+ maxWidth: "none",
16224
+ interactive: true,
16225
+ allowHTML: true,
16226
+ appendTo: () => document.body,
16227
+ content: () => content,
16228
+ onShown: () => {
16229
+ this.open = true;
16230
+ },
16231
+ onHidden: () => {
16232
+ this.open = false;
16233
+ },
16234
+ ...options
16235
+ });
16236
+ });
16237
+ } else {
16238
+ console.error(`Popover target '${target} not found.'`);
16239
+ }
16240
+ },
16241
+ show(event) {
16242
+ if (!event || this.eventTarget(event) === this.targetEl && this.popoverInstance) {
16243
+ this.popoverInstance.show();
16244
+ }
16245
+ },
16246
+ hide(event) {
16247
+ if (!event || this.eventTarget(event) === this.targetEl && this.popoverInstance) {
16248
+ this.popoverInstance.hide();
16249
+ }
16250
+ },
16251
+ toggle(event) {
16252
+ if (!event || this.eventTarget(event) === this.targetEl && this.popoverInstance) {
16253
+ this.open ? this.popoverInstance.hide() : this.popoverInstance.show();
16254
+ }
16255
+ },
16256
+ destroy() {
16257
+ if (this.popoverInstance) {
16258
+ this.popoverInstance.destroy();
16259
+ }
16260
+ },
16261
+ eventTarget(event) {
16262
+ if (event.detail.target) {
16263
+ if (typeof event.detail.target === "string") {
16264
+ return document.querySelector(event.detail.target);
16265
+ } else {
16266
+ return event.detail.target;
16267
+ }
16268
+ } else {
16269
+ return event.target;
16270
+ }
16271
+ },
16272
+ get theme() {
16273
+ return `coco-popover-${this.$root.getAttribute("data-theme")}`;
16274
+ },
16275
+ get popoverInstance() {
16276
+ if (this.targetEl) {
16277
+ return this.targetEl._tippy;
16278
+ }
16279
+ }
16280
+ };
16281
+ });
16282
+
16283
+ // ../../components/coco/messaging/popover/popover_content.js
16284
+ var popover_content_exports = {};
16285
+ __export(popover_content_exports, {
16286
+ default: () => popover_content_default
16287
+ });
16288
+ var popover_content_default = CocoComponent("popoverContent", () => {
16289
+ return {
16290
+ init() {
16291
+ this.hide = this.hide.bind(this);
16292
+ this.show = this.show.bind(this);
16293
+ this.toggle = this.toggle.bind(this);
16294
+ },
16295
+ hide() {
16296
+ this.$dispatch("popover:hide", { target: this.$root.__popover_target });
16297
+ },
16298
+ show() {
16299
+ this.$dispatch("popover:show", { target: this.$root.__popover_target });
16300
+ },
16301
+ toggle() {
16302
+ this.$dispatch("popover:toggle", { target: this.$root.__popover_target });
16303
+ }
16304
+ };
16305
+ });
16306
+
16198
16307
  // ../../components/coco/messaging/snackbar/snackbar.js
16199
16308
  var snackbar_exports = {};
16200
16309
  __export(snackbar_exports, {
@@ -16401,7 +16510,7 @@ var tooltip_default2 = CocoComponent("tooltip", () => {
16401
16510
  });
16402
16511
 
16403
16512
  // import-glob:/Users/mark/Code/coveragebook/coco/app/assets/js/base|@components/messaging/**/*.js
16404
- var modules7 = [alert_exports, notice_exports, snackbar_exports, system_banner_exports, toast_exports, tooltip_exports];
16513
+ var modules7 = [alert_exports, notice_exports, popover_exports, popover_content_exports, snackbar_exports, system_banner_exports, toast_exports, tooltip_exports];
16405
16514
  var __default7 = modules7;
16406
16515
 
16407
16516
  // ../../components/coco/modals/modal/modal.js
@@ -16,7 +16,7 @@ export default function (Alpine) {
16
16
  const settings = buildConfig(modifiers);
17
17
  const result = expression ? evaluate(expression) : {};
18
18
  const directiveConfig = isObject(result) ? result : {};
19
- let { triggerTarget, contentTarget, anchorTarget, ...config } =
19
+ let { triggerTarget, contentTarget, anchorTarget, flip, ...config } =
20
20
  directiveConfig;
21
21
 
22
22
  contentTarget =
@@ -61,6 +61,9 @@ export default function (Alpine) {
61
61
  interactive: true,
62
62
  animation: false,
63
63
  maxWidth: 380,
64
+ popperOptions: {
65
+ modifiers: [{ name: "flip", enabled: flip !== false }],
66
+ },
64
67
 
65
68
  triggerTarget,
66
69
  content,
@@ -55,6 +55,7 @@ module Coco
55
55
  dd.accepts_option :placement,
56
56
  from: %w[top top-start top-end right right-start right-end bottom bottom-start bottom-end left left-start left-end auto auto-start auto-end],
57
57
  private: true
58
+ dd.accepts_option :flip, from: [true, false], default: true, private: true
58
59
  end
59
60
 
60
61
  renders_one :text, Coco::Content
@@ -154,7 +155,7 @@ module Coco
154
155
  if dropdown? || confirm?
155
156
  {
156
157
  data: x_data("buttonDropdown"),
157
- dropdown: jsify_data({placement: get_option_value(:dropdown, :placement)}.compact),
158
+ dropdown: jsify_data({placement: get_option_value(:dropdown, :placement), flip: get_option_value(:dropdown, :flip)}.compact),
158
159
  bind: "root"
159
160
  }
160
161
  end
@@ -16,21 +16,17 @@ module Coco
16
16
  divider: ->(**kwargs) { tag.div(class: "divider") },
17
17
  html: ->(&block) { block.call },
18
18
  button: ->(*args, **kwargs, &block) { coco_button(*args, **button_kwargs(kwargs, :button), &block) },
19
- menu_button: ->(*args, **kwargs, &block) { instantiate_button(:menu, *args, **kwargs, &block) },
20
- color_picker_button: ->(*args, **kwargs, &block) { instantiate_button(:color_picker, *args, **kwargs, &block) },
21
- image_picker_button: ->(*args, **kwargs, &block) { instantiate_button(:image_picker, *args, **kwargs, &block) },
22
- layout_picker_button: ->(*args, **kwargs, &block) { instantiate_button(:layout_picker, *args, **kwargs, &block) }
19
+ menu_button: ->(*args, **kwargs, &block) { instantiate_button(:menu, args.first, nil, **kwargs, &block) },
20
+ color_picker_button: ->(*args, **kwargs, &block) { instantiate_button(:color_picker, args.first, nil, **kwargs, &block) },
21
+ image_picker_button: ->(*args, **kwargs, &block) { instantiate_button(:image_picker, args.first, nil, **kwargs, &block) },
22
+ layout_picker_button: ->(*args, **kwargs, &block) { instantiate_button(:layout_picker, args.first, nil, **kwargs, &block) }
23
23
  }
24
24
  end
25
25
 
26
26
  def instantiate_button(type, *args, **kwargs, &block)
27
- href, content = if block
28
- [args.first, nil]
29
- else
30
- (args.size == 1) ? [nil, args.first] : args[0..2].reverse!
31
- end
32
- component = BUTTON_TYPES[type].constantize.new(href: href, **button_kwargs(kwargs, type))
33
- component.with_content(content) if !block && content.present?
27
+ href, content = (args.size == 1) ? [nil, args.first] : args[0..2].reverse!
28
+ component = BUTTON_TYPES[type].constantize.new(text: content, href: href, **button_kwargs(kwargs, type))
29
+ component.with_text(content) if content.present?
34
30
  component
35
31
  end
36
32
 
@@ -0,0 +1,37 @@
1
+ @layer components {
2
+ [data-coco][data-component="popover"] {
3
+ display: none;
4
+ pointer-events: none;
5
+ }
6
+ }
7
+
8
+ /* Tippy popover theme */
9
+ .tippy-box[data-theme^="coco-popover-"] {
10
+ @apply text-content-light-1 text-para-md border-none shadow-xl rounded-lg p-6 font-normal;
11
+ width: fit-content;
12
+
13
+ .tippy-svg-arrow > svg:first-child {
14
+ @apply hidden;
15
+ }
16
+ }
17
+
18
+ .tippy-box[data-theme~="coco-popover-neutral"] {
19
+ @apply bg-background-light-1 text-content-dark-1 border border-solid border-gray-blend-100;
20
+
21
+ .tippy-svg-arrow > svg:first-child {
22
+ @apply block;
23
+ @apply fill-coco-gray-300;
24
+ }
25
+
26
+ .tippy-svg-arrow > svg:last-child {
27
+ @apply fill-background-light-1;
28
+ }
29
+ }
30
+
31
+ .tippy-box[data-theme~="coco-popover-info"] {
32
+ @apply bg-background-info antialiased;
33
+
34
+ .tippy-svg-arrow > svg:last-child {
35
+ @apply fill-background-info;
36
+ }
37
+ }
@@ -0,0 +1,10 @@
1
+ <%= render component_tag(x: {
2
+ data: "popover({target: #{target.to_json}, trigger: #{trigger.to_json}, options: #{options.to_json}})",
3
+ "@popover:show.document": "show",
4
+ "@popover:hide.document": "hide",
5
+ "@popover:toggle.document": "toggle"
6
+ }) do %>
7
+ <div x-data="popoverContent">
8
+ <%= content %>
9
+ </div>
10
+ <% end %>
@@ -0,0 +1,103 @@
1
+ import { CocoComponent } from "@assets/js/coco/component";
2
+ import tippy from "@js/base/tippy";
3
+ import { getData } from "@helpers/alpine";
4
+
5
+ export default CocoComponent("popover", ({ target, trigger, options = {} }) => {
6
+ return {
7
+ targetEl: null,
8
+ open: false,
9
+
10
+ init() {
11
+ this.targetEl = target
12
+ ? document.querySelector(target)
13
+ : this.$el.parentElement;
14
+
15
+ if (this.targetEl) {
16
+ this.targetEl.coco_popover = this;
17
+
18
+ if (trigger == "click") {
19
+ this.targetEl.style.cursor = "pointer";
20
+ }
21
+
22
+ this.$nextTick(() => {
23
+ const content = this.$el.firstElementChild;
24
+ content.__popover_target = this.targetEl;
25
+
26
+ tippy(this.targetEl, {
27
+ trigger,
28
+ theme: this.theme,
29
+ maxWidth: "none",
30
+ interactive: true,
31
+ allowHTML: true,
32
+ appendTo: () => document.body,
33
+ content: () => content,
34
+ onShown: () => {
35
+ this.open = true;
36
+ },
37
+ onHidden: () => {
38
+ this.open = false;
39
+ },
40
+ ...options,
41
+ });
42
+ });
43
+ } else {
44
+ console.error(`Popover target '${target} not found.'`);
45
+ }
46
+ },
47
+
48
+ show(event) {
49
+ if (
50
+ !event ||
51
+ (this.eventTarget(event) === this.targetEl && this.popoverInstance)
52
+ ) {
53
+ this.popoverInstance.show();
54
+ }
55
+ },
56
+
57
+ hide(event) {
58
+ if (
59
+ !event ||
60
+ (this.eventTarget(event) === this.targetEl && this.popoverInstance)
61
+ ) {
62
+ this.popoverInstance.hide();
63
+ }
64
+ },
65
+
66
+ toggle(event) {
67
+ if (
68
+ !event ||
69
+ (this.eventTarget(event) === this.targetEl && this.popoverInstance)
70
+ ) {
71
+ this.open ? this.popoverInstance.hide() : this.popoverInstance.show();
72
+ }
73
+ },
74
+
75
+ destroy() {
76
+ if (this.popoverInstance) {
77
+ this.popoverInstance.destroy();
78
+ }
79
+ },
80
+
81
+ eventTarget(event) {
82
+ if (event.detail.target) {
83
+ if (typeof event.detail.target === "string") {
84
+ return document.querySelector(event.detail.target);
85
+ } else {
86
+ return event.detail.target;
87
+ }
88
+ } else {
89
+ return event.target;
90
+ }
91
+ },
92
+
93
+ get theme() {
94
+ return `coco-popover-${this.$root.getAttribute("data-theme")}`;
95
+ },
96
+
97
+ get popoverInstance() {
98
+ if (this.targetEl) {
99
+ return this.targetEl._tippy;
100
+ }
101
+ },
102
+ };
103
+ });
@@ -0,0 +1,37 @@
1
+ module Coco
2
+ class Popover < Coco::Component
3
+ include Concerns::AcceptsOptions
4
+
5
+ accepts_option :theme, from: ["netural", "info"], default: "neutral"
6
+ accepts_option :trigger, from: ["click", "manual", "hover"], default: "click"
7
+ accepts_option :placement,
8
+ from: %w[top top-start top-end right right-start right-end bottom bottom-start bottom-end left left-start left-end auto auto-start auto-end],
9
+ default: "auto",
10
+ private: true
11
+
12
+ before_initialize do |kwargs|
13
+ [:placement, :trigger, :theme].each do |key|
14
+ kwargs[key] = kwargs[key].to_s.tr("_", "-") if kwargs.key?(key)
15
+ end
16
+ kwargs
17
+ end
18
+
19
+ attr_reader :target
20
+
21
+ def initialize(target: nil, options: {}, **)
22
+ @target = target
23
+ @options = options
24
+ end
25
+
26
+ def trigger
27
+ (get_option_value(:trigger) == "hover") ? "mouseenter focus" : get_option_value(:trigger)
28
+ end
29
+
30
+ def options
31
+ {
32
+ placement: get_option_value(:placement),
33
+ **@options
34
+ }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ import { CocoComponent } from "@assets/js/coco/component";
2
+ import { getData } from "@helpers/alpine";
3
+
4
+ export default CocoComponent("popoverContent", () => {
5
+ return {
6
+ init() {
7
+ this.hide = this.hide.bind(this);
8
+ this.show = this.show.bind(this);
9
+ this.toggle = this.toggle.bind(this);
10
+ },
11
+
12
+ hide() {
13
+ this.$dispatch("popover:hide", { target: this.$root.__popover_target });
14
+ },
15
+
16
+ show() {
17
+ this.$dispatch("popover:show", { target: this.$root.__popover_target });
18
+ },
19
+
20
+ toggle() {
21
+ this.$dispatch("popover:toggle", { target: this.$root.__popover_target });
22
+ },
23
+ };
24
+ });
@@ -27,8 +27,8 @@ module Coco
27
27
  render(Coco::ButtonGroup.new(**), &)
28
28
  end
29
29
 
30
- def coco_menu_button(**, &)
31
- render(Coco::MenuButton.new(**), &)
30
+ def coco_menu_button(text = nil, **, &)
31
+ render(Coco::MenuButton.new(text: text, **), &)
32
32
  end
33
33
 
34
34
  def coco_menu_item(type, **, &)
@@ -166,6 +166,10 @@ module Coco
166
166
  render(Coco::SystemBanner.new(**), &)
167
167
  end
168
168
 
169
+ def coco_popover(**, &)
170
+ render(Coco::Popover.new(**), &)
171
+ end
172
+
169
173
  # Modals
170
174
 
171
175
  def coco_modal(name = "default", **, &)
data/lib/coco.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Coco
2
- VERSION = "0.17.7"
2
+ VERSION = "0.18.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coveragebook_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.7
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Perkins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-18 00:00:00.000000000 Z
11
+ date: 2024-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -1647,6 +1647,11 @@ files:
1647
1647
  - app/components/coco/messaging/notice/notice.html.erb
1648
1648
  - app/components/coco/messaging/notice/notice.js
1649
1649
  - app/components/coco/messaging/notice/notice.rb
1650
+ - app/components/coco/messaging/popover/popover.css
1651
+ - app/components/coco/messaging/popover/popover.html.erb
1652
+ - app/components/coco/messaging/popover/popover.js
1653
+ - app/components/coco/messaging/popover/popover.rb
1654
+ - app/components/coco/messaging/popover/popover_content.js
1650
1655
  - app/components/coco/messaging/snackbar/snackbar.css
1651
1656
  - app/components/coco/messaging/snackbar/snackbar.html.erb
1652
1657
  - app/components/coco/messaging/snackbar/snackbar.js