coveragebook_components 0.7.10 → 0.8.0.beta.2

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/build/coco/app.css +1160 -151
  3. data/app/assets/build/coco/app.js +263 -47
  4. data/app/assets/build/coco/book.css +26 -0
  5. data/app/assets/css/app/tippy.css +4 -0
  6. data/app/components/coco/app/blocks/header/header.css +47 -0
  7. data/app/components/coco/app/blocks/header/header.html.erb +30 -0
  8. data/app/components/coco/app/blocks/header/header.js +11 -0
  9. data/app/components/coco/app/blocks/header/header.rb +35 -0
  10. data/app/components/coco/app/blocks/nav_drawer/nav_drawer.css +48 -3
  11. data/app/components/coco/app/blocks/nav_drawer/nav_drawer.html.erb +14 -6
  12. data/app/components/coco/app/blocks/nav_drawer/nav_drawer.js +18 -1
  13. data/app/components/coco/app/blocks/nav_drawer/nav_drawer.rb +26 -1
  14. data/app/components/coco/app/blocks/sidebar_nav/sidebar_nav.css +104 -0
  15. data/app/components/coco/app/blocks/sidebar_nav/sidebar_nav.html.erb +42 -0
  16. data/app/components/coco/app/blocks/sidebar_nav/sidebar_nav.js +28 -0
  17. data/app/components/coco/app/blocks/sidebar_nav/sidebar_nav.rb +28 -0
  18. data/app/components/coco/app/blocks/sidebar_nav_item/sidebar_nav_item.css +165 -0
  19. data/app/components/coco/app/blocks/sidebar_nav_item/sidebar_nav_item.html.erb +43 -0
  20. data/app/components/coco/app/blocks/sidebar_nav_item/sidebar_nav_item.js +41 -0
  21. data/app/components/coco/app/blocks/sidebar_nav_item/sidebar_nav_item.rb +98 -0
  22. data/app/components/coco/app/elements/alert/alert.css +114 -18
  23. data/app/components/coco/app/elements/alert/alert.html.erb +5 -4
  24. data/app/components/coco/app/elements/alert/alert.js +13 -3
  25. data/app/components/coco/app/elements/alert/alert.rb +10 -0
  26. data/app/components/coco/app/elements/button/button.css +59 -0
  27. data/app/components/coco/app/elements/button/button.rb +2 -1
  28. data/app/components/coco/app/elements/button_group/button_group.rb +4 -0
  29. data/app/components/coco/app/elements/color_picker/color_picker.rb +1 -1
  30. data/app/components/coco/app/elements/menu/menu.css +3 -1
  31. data/app/components/coco/app/elements/menu/menu.html.erb +1 -1
  32. data/app/components/coco/app/elements/menu/menu.rb +2 -1
  33. data/app/components/coco/app/elements/menu_items/user_profile/user_profile.css +22 -0
  34. data/app/components/coco/app/elements/menu_items/user_profile/user_profile.html.erb +17 -0
  35. data/app/components/coco/app/elements/menu_items/user_profile/user_profile.rb +20 -0
  36. data/app/components/coco/app/elements/notice/notice.css +4 -0
  37. data/app/components/coco/app/elements/snackbar/snackbar.css +8 -1
  38. data/app/components/coco/app/elements/snackbar/snackbar.rb +2 -2
  39. data/app/components/coco/app/elements/system_banner/system_banner.html.erb +2 -1
  40. data/app/components/coco/app/elements/system_banner/system_banner.js +35 -2
  41. data/app/components/coco/app/elements/system_banner/system_banner.rb +47 -3
  42. data/app/components/coco/app/layouts/application/application.css +104 -4
  43. data/app/components/coco/app/layouts/application/application.html.erb +28 -7
  44. data/app/components/coco/app/layouts/application/application.js +16 -0
  45. data/app/components/coco/app/layouts/application/application.rb +11 -3
  46. data/app/components/coco/base/avatar/avatar.css +25 -0
  47. data/app/components/coco/base/avatar/avatar.rb +20 -0
  48. data/app/components/coco/base/modal/modal.html.erb +1 -1
  49. data/app/components/coco/concerns/accepts_tag_attributes.rb +6 -2
  50. data/app/components/coco/concerns/acts_as_button_group.rb +5 -0
  51. data/app/helpers/coco/app_helper.rb +16 -0
  52. data/app/helpers/coco/base_helper.rb +4 -0
  53. data/config/tokens.cjs +4 -1
  54. data/lib/coco.rb +1 -1
  55. metadata +22 -10
  56. data/app/components/coco/app/blocks/banner/banner.css +0 -5
  57. data/app/components/coco/app/blocks/banner/banner.rb +0 -8
  58. data/app/components/coco/app/blocks/nav_bar/nav_bar.css +0 -51
  59. data/app/components/coco/app/blocks/nav_bar/nav_bar.html.erb +0 -23
  60. data/app/components/coco/app/blocks/nav_bar/nav_bar.js +0 -31
  61. data/app/components/coco/app/blocks/nav_bar/nav_bar.rb +0 -19
@@ -2,6 +2,23 @@ import { CocoComponent } from "@js/coco";
2
2
 
3
3
  export default CocoComponent("appNavDrawer", () => {
4
4
  return {
5
- open: false,
5
+ active: false,
6
+
7
+ init() {
8
+ this.$watch("drawerOpen", (open) => {
9
+ if (open) {
10
+ this.$refs.content.scrollTop = 0;
11
+ setTimeout(() => {
12
+ this.active = true;
13
+ }, 200);
14
+ } else {
15
+ this.active = false;
16
+ }
17
+ });
18
+ },
19
+
20
+ close() {
21
+ this.drawerOpen = false;
22
+ },
6
23
  };
7
24
  });
@@ -2,7 +2,32 @@ module Coco
2
2
  module App
3
3
  module Blocks
4
4
  class NavDrawer < Coco::Component
5
- include Coco::AppHelper
5
+ renders_many :items, types: {
6
+ html: ->(&block) { block.call },
7
+ divider: ->(&block) { tag.div class: "nav-drawer-divider" },
8
+ heading: ->(text, **kwargs, &block) do
9
+ tag.h4(text, class: "nav-drawer-heading")
10
+ end,
11
+ button: ->(*args, **kwargs, &block) do
12
+ coco_button(*args, **kwargs, theme: nil, size: :md, fit: :full, class: "nav-drawer-button", &block)
13
+ end
14
+ }
15
+
16
+ def with_divider(...)
17
+ with_item_divider(...)
18
+ end
19
+
20
+ def with_html(...)
21
+ with_item_html(...)
22
+ end
23
+
24
+ def with_button(...)
25
+ with_item_button(...)
26
+ end
27
+
28
+ def with_heading(...)
29
+ with_item_heading(...)
30
+ end
6
31
  end
7
32
  end
8
33
  end
@@ -0,0 +1,104 @@
1
+ @layer components {
2
+ [data-coco][data-component="app-sidebar-nav"] {
3
+ @apply bg-background-dark-2 antialiased;
4
+ @apply w-full h-14 grid grid-rows-1;
5
+ grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(0, 1fr);
6
+
7
+ .nav-action {
8
+ .nav-item-label {
9
+ @apply hidden !text-content-primary-inverse-vivid;
10
+ }
11
+ }
12
+
13
+ .nav-actions-button {
14
+ @apply bg-content-primary-inverse-vivid transition-all text-content-light-1;
15
+ @apply h-10 w-10 flex items-center justify-center mx-auto rounded-full cursor-pointer;
16
+
17
+ &:hover {
18
+ transform: scale(1.05);
19
+ }
20
+
21
+ .coco-icon {
22
+ @apply h-6 w-6;
23
+ }
24
+ }
25
+
26
+ .nav-item {
27
+ @apply hidden sm:contents h-full justify-center w-full;
28
+ }
29
+
30
+ .mobile-nav-item {
31
+ @apply flex sm:contents;
32
+ }
33
+
34
+ @media screen(sm) {
35
+ @apply sidebar-nav-vertical-condensed;
36
+ }
37
+
38
+ @media screen(lg) {
39
+ @apply sidebar-nav-vertical;
40
+ }
41
+
42
+ @media screen(letterbox) {
43
+ @apply sidebar-nav-vertical-condensed;
44
+ }
45
+ }
46
+
47
+ .sidebar-nav-action {
48
+ @apply flex items-start p-1;
49
+
50
+ .sidebar-nav-action-icon {
51
+ @apply flex-none mr-3 text-content-light-1;
52
+ }
53
+
54
+ .sidebar-nav-action-detail {
55
+ @apply w-full text-left;
56
+ }
57
+
58
+ .sidebar-nav-action-label {
59
+ @apply mb-0.5 font-semibold text-content-light-1;
60
+ }
61
+
62
+ .sidebar-nav-action-description {
63
+ @apply text-para-xs;
64
+ }
65
+ }
66
+
67
+ .sidebar-nav-menu a.sidebar-nav-actions-item {
68
+ width: 320px;
69
+ }
70
+ }
71
+
72
+ @layer utilities {
73
+ .sidebar-nav-vertical-condensed {
74
+ @apply block w-18 h-full;
75
+
76
+ .nav-action .nav-item-label {
77
+ @apply block;
78
+ }
79
+
80
+ .nav-actions-button {
81
+ @apply h-14 w-14 mb-2;
82
+
83
+ .coco-icon {
84
+ @apply w-8 h-8;
85
+ }
86
+ }
87
+ }
88
+
89
+ .sidebar-nav-vertical {
90
+ @apply block w-24 h-full;
91
+
92
+ .nav-action .nav-item-label {
93
+ @apply block;
94
+ }
95
+
96
+ .nav-actions-button {
97
+ @apply h-[72px] w-[72px];
98
+
99
+ .coco-icon {
100
+ @apply h-10 w-10;
101
+ }
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,42 @@
1
+ <%= render component_tag(:nav, x: {data: "appSidebarNav"}) do %>
2
+ <% if actions? %>
3
+ <div class="nav-item mobile-nav-item nav-action">
4
+ <%= render Coco::App::Blocks::SidebarNavItem.new(label: "Add", emphasise: true) do |item| %>
5
+ <% item.with_icon do %>
6
+ <span class="nav-actions-button">
7
+ <%= coco_icon :plus, size: :xl %>
8
+ </span>
9
+ <% end %>
10
+
11
+ <% item.with_menu do %>
12
+ <% actions_data.each do |action| %>
13
+ <%= coco_link(action[:href],
14
+ **action.except(:label, :description, :href, :icon),
15
+ theme: nil,
16
+ class: "sidebar-nav-menu-item sidebar-nav-actions-item") do %>
17
+ <div class="sidebar-nav-action">
18
+ <div class="sidebar-nav-action-icon">
19
+ <%= coco_icon(action[:icon], size: :md) %>
20
+ </div>
21
+ <div class="sidebar-nav-action-detail">
22
+ <h4 class="sidebar-nav-action-label">
23
+ <%= action[:label] %>
24
+ </h4>
25
+ <% if action.key?(:description) %>
26
+ <div class="sidebar-nav-action-description">
27
+ <%= raw action[:description] %>
28
+ </div>
29
+ <% end %>
30
+ </div>
31
+ </div>
32
+ <% end %>
33
+ <% end %>
34
+ <% end %>
35
+ <% end %>
36
+ </div>
37
+ <% end %>
38
+
39
+ <% items.each do |item| %>
40
+ <%= item %>
41
+ <% end %>
42
+ <% end %>
@@ -0,0 +1,28 @@
1
+ import tokens from "@config/tokens.cjs";
2
+ import { CocoComponent } from "@js/coco";
3
+
4
+ const mobileMaxWidth = parseInt(tokens.app.screens.sm, 10);
5
+
6
+ export default CocoComponent("appSidebarNav", () => {
7
+ return {
8
+ sizeObserver: null,
9
+ mobileLayout: true,
10
+
11
+ init() {
12
+ this.sizeObserver = new ResizeObserver(
13
+ Alpine.throttle((entries) => {
14
+ this.$nextTick(() => {
15
+ const bodyWidth = entries[0].contentRect.width;
16
+ this.mobileLayout = bodyWidth < mobileMaxWidth;
17
+ });
18
+ }, 20)
19
+ );
20
+
21
+ this.sizeObserver.observe(document.documentElement);
22
+ },
23
+
24
+ destroy() {
25
+ this.sizeObserver && this.sizeObserver.disconnect();
26
+ },
27
+ };
28
+ });
@@ -0,0 +1,28 @@
1
+ module Coco
2
+ module App
3
+ module Blocks
4
+ class SidebarNav < Coco::Component
5
+ renders_many :actions, ->(label, href, icon:, **kwargs) do
6
+ @actions_data << {label: label, href: href, icon: icon, **kwargs}
7
+ end
8
+
9
+ renders_many :items, ->(label, href, show_on_mobile: true, **kwargs, &block) do
10
+ tag.div class: ["nav-item", ("mobile-nav-item" if show_on_mobile)] do
11
+ render Coco::App::Blocks::SidebarNavItem.new(
12
+ label: label,
13
+ href: href,
14
+ active: helpers.current_page?(href),
15
+ **kwargs
16
+ ), &block
17
+ end
18
+ end
19
+
20
+ attr_reader :actions_data
21
+
22
+ def initialize(**)
23
+ @actions_data = []
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,165 @@
1
+ @layer components {
2
+ [data-coco][data-component="app-sidebar-nav-item"] {
3
+ @apply bg-background-dark-2 text-content-dark-muted text-center no-underline transition-colors flex-none;
4
+ @apply flex items-center justify-center px-4 w-full flex-col;
5
+
6
+ &[href] {
7
+ @apply hover:text-content-light-1;
8
+ }
9
+
10
+ .nav-item-icon {
11
+ @apply block mx-auto transition-colors;
12
+ @apply w-6 h-6;
13
+ }
14
+
15
+ .nav-item-label {
16
+ @apply block font-semibold text-label-xxs transition-colors mt-0.5;
17
+ }
18
+
19
+ &.emphasised {
20
+ .nav-item-icon {
21
+ @apply sm:text-content-primary-inverse-vivid;
22
+ }
23
+ }
24
+
25
+ &.active {
26
+ @apply text-content-light-1;
27
+ }
28
+
29
+ &:hover {
30
+ .nav-item-icon {
31
+ @apply text-content-light-1;
32
+ }
33
+
34
+ &.emphasised .nav-item-icon {
35
+ @apply sm:text-content-primary-inverse-vivid;
36
+ }
37
+
38
+ .nav-item-label {
39
+ @apply text-content-light-1;
40
+ }
41
+ }
42
+
43
+ @media screen(sm) {
44
+ @apply sidebar-nav-item-vertical-condensed;
45
+ }
46
+
47
+ @media screen(lg) {
48
+ @apply sidebar-nav-item-vertical;
49
+ }
50
+
51
+ @media screen(letterbox) {
52
+ @apply sidebar-nav-item-vertical-condensed;
53
+ }
54
+ }
55
+
56
+ .sidebar-nav-menu {
57
+ @apply bg-content-dark-2 shadow-md sm:rounded-r-md overflow-hidden divide-y divide-content-dark-muted/20;
58
+
59
+ [data-placement="bottom"] & {
60
+ @apply rounded-b-md;
61
+ }
62
+
63
+ [data-placement="top"] & {
64
+ @apply rounded-t-md;
65
+ }
66
+
67
+ .sidebar-nav-menu-select {
68
+ @apply focus:ring-0 bg-transparent hover:bg-content-dark-1 active:bg-content-dark-1 border-0 text-content-light-2 w-full font-semibold;
69
+ }
70
+
71
+ .sidebar-nav-menu-item {
72
+ @apply flex w-full items-center p-3 text-center no-underline bg-content-dark-2 text-content-dark-muted transition;
73
+ }
74
+
75
+ .coco-link.sidebar-nav-menu-item {
76
+ @apply hover:bg-content-dark-1;
77
+ }
78
+
79
+ .sidebar-nav-menu-item-text {
80
+ @apply truncate min-w-0 font-semibold text-content-light-1 transition-colors;
81
+ }
82
+
83
+ .sidebar-nav-menu-item-qualifier {
84
+ @apply ml-auto text-label-sm flex-none pl-4;
85
+ }
86
+
87
+ @apply sidebar-nav-menu-large;
88
+
89
+ @media screen(sm) {
90
+ @apply sidebar-nav-menu-small;
91
+ }
92
+
93
+ @media screen(lg) {
94
+ @apply sidebar-nav-menu-large;
95
+ }
96
+
97
+ @media screen(letterbox) {
98
+ @apply sidebar-nav-menu-small;
99
+ }
100
+ }
101
+ }
102
+
103
+ @layer utilities {
104
+ .sidebar-nav-item-vertical-condensed {
105
+ @apply block px-2 py-4;
106
+
107
+ &[href] {
108
+ @apply hover:bg-content-dark-1;
109
+ }
110
+
111
+ .nav-item-icon {
112
+ @apply w-7 h-7;
113
+ }
114
+
115
+ .nav-item-label {
116
+ @apply mt-1 text-label-xxs;
117
+ }
118
+
119
+ &.active {
120
+ @apply bg-background-dark-3 hover:bg-background-dark-3 text-content-light-1;
121
+ }
122
+ }
123
+
124
+ .sidebar-nav-item-vertical {
125
+ @apply py-5;
126
+
127
+ &[href] {
128
+ @apply hover:bg-content-dark-1;
129
+ }
130
+
131
+ .nav-item-icon {
132
+ @apply w-8 h-8;
133
+ }
134
+
135
+ .nav-item-label {
136
+ @apply text-label-xs;
137
+ }
138
+
139
+ &.active {
140
+ @apply bg-background-dark-3 hover:bg-background-dark-3 text-content-light-1;
141
+ }
142
+ }
143
+
144
+ .sidebar-nav-menu-small {
145
+ .sidebar-nav-menu-select {
146
+ @apply text-sm;
147
+ }
148
+
149
+ .sidebar-nav-menu-item {
150
+ @apply text-sm;
151
+ width: 240px;
152
+ }
153
+ }
154
+
155
+ .sidebar-nav-menu-large {
156
+ .sidebar-nav-menu-select {
157
+ @apply text-base;
158
+ }
159
+
160
+ .sidebar-nav-menu-item {
161
+ @apply text-base;
162
+ width: 280px;
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,43 @@
1
+ <%= render component_tag(
2
+ x: {
3
+ data: "appSidebarNavItem",
4
+ "on:click": "if (menu) $event.preventDefault()"
5
+ },
6
+ class: {
7
+ active: active?,
8
+ emphasised: emphasise?
9
+ }) do %>
10
+ <%= icon %>
11
+ <label class="nav-item-label">
12
+ <%= label %>
13
+ </label>
14
+ <% if menu? || menu_items? %>
15
+ <template x-ref="menu">
16
+ <div class="sidebar-nav-menu" x-cloak>
17
+ <% if menu? %>
18
+ <%= menu %>
19
+ <% else %>
20
+ <%= safe_join(menu_items) %>
21
+
22
+ <% if dropdown_menu_items.any? %>
23
+ <select
24
+ x-data
25
+ class="sidebar-nav-menu-select"
26
+ @change="$dispatch('navigate', {url: $el.value})">
27
+ <% if menu_select_placeholder.present? %>
28
+ <option disabled="disabled" selected="selected"><%= menu_select_placeholder %></option>
29
+ <% end %>
30
+ <% dropdown_menu_items.each do |item| %>
31
+ <option value="<%= item[:href] %>">
32
+ <%= item[:label] %>&nbsp;&nbsp;<%= "(#{item[:qualifier]})" if item[:qualifier].present? %>
33
+ </option>
34
+ <% end %>
35
+ </select>
36
+ <% end %>
37
+
38
+ <%= menu_action %>
39
+ <% end %>
40
+ </div>
41
+ </template>
42
+ <% end %>
43
+ <% end %>
@@ -0,0 +1,41 @@
1
+ import tippy from "@libs/tippy";
2
+ import { CocoComponent } from "@js/coco";
3
+
4
+ export default CocoComponent("appSidebarNavItem", () => {
5
+ return {
6
+ menu: null,
7
+
8
+ init() {
9
+ if (this.$refs.menu) {
10
+ this.menu = tippy(this.$el, {
11
+ theme: "coco-naked-dropdown",
12
+ placement: this.menuPlacement,
13
+ arrow: false,
14
+ offset: [0, 0],
15
+ trigger: "click",
16
+ interactive: true,
17
+ maxWidth: null,
18
+ content: () => {
19
+ return this.$refs.menu.innerHTML;
20
+ },
21
+ });
22
+ }
23
+
24
+ this.$watch("mobileLayout", () => {
25
+ this.onOrientationChange();
26
+ });
27
+ },
28
+
29
+ onOrientationChange() {
30
+ if (this.menu) {
31
+ this.menu.setProps({
32
+ placement: this.menuPlacement,
33
+ });
34
+ }
35
+ },
36
+
37
+ get menuPlacement() {
38
+ return this.mobileLayout ? "top" : "right-start";
39
+ },
40
+ };
41
+ });
@@ -0,0 +1,98 @@
1
+ module Coco
2
+ module App
3
+ module Blocks
4
+ class SidebarNavItem < Coco::Component
5
+ tag_name :a
6
+ tag_attr :href
7
+
8
+ renders_one :icon
9
+ renders_one :menu
10
+
11
+ renders_many :menu_items, ->(label, href, **kwargs) do
12
+ data = {label: label, href: href, **kwargs}
13
+ if menu_items.size < max_menu_items
14
+ SidebarNavMenuItem.new(**data)
15
+ else
16
+ dropdown_menu_items << data
17
+ nil
18
+ end
19
+ end
20
+
21
+ renders_one :menu_action, ->(*args, **kwargs, &block) do
22
+ tag.div class: "sidebar-nav-menu-item" do
23
+ coco_button(*args, theme: :primary, size: :sm, fit: :full, **kwargs, &block)
24
+ end
25
+ end
26
+
27
+ before_render do
28
+ if @icon && !icon?
29
+ with_icon do
30
+ tag.span(class: "nav-item-icon") do
31
+ coco_icon(@icon, size: :full)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ attr_reader :label, :tooltip, :dropdown_menu_items, :max_menu_items
38
+
39
+ def initialize(label:, icon: nil, emphasise: false, active: false, tooltip: nil, menu_select_placeholder: nil, max_menu_items: 6, **)
40
+ @label = label
41
+ @icon = icon
42
+ @emphasise = emphasise
43
+ @tooltip = tooltip
44
+ @active = active
45
+ @max_menu_items = max_menu_items
46
+ @menu_select_placeholder = menu_select_placeholder
47
+ @dropdown_menu_items = []
48
+ end
49
+
50
+ def tooltip?
51
+ tooltip.present?
52
+ end
53
+
54
+ def active?
55
+ @active == true
56
+ end
57
+
58
+ def emphasise?
59
+ @emphasise == true
60
+ end
61
+
62
+ def menu_select_placeholder
63
+ @menu_select_placeholder || "More items..."
64
+ end
65
+
66
+ class SidebarNavMenuItem < Coco::Component
67
+ attr_reader :icon, :href, :modal
68
+
69
+ def initialize(label:, href:, icon: nil, modal: nil, qualifier: nil, **)
70
+ @label = label
71
+ @href = href
72
+ @icon = icon
73
+ @modal = modal
74
+ @qualifier = qualifier
75
+ end
76
+
77
+ def call
78
+ coco_link(href, theme: nil, modal: modal, class: "sidebar-nav-menu-item") do
79
+ label
80
+ end
81
+ end
82
+
83
+ def label
84
+ tag.span(@label, class: "sidebar-nav-menu-item-text") + qualifier
85
+ end
86
+
87
+ def qualifier
88
+ if @icon || @qualifier
89
+ tag.span(class: "sidebar-nav-menu-item-qualifier") do
90
+ @icon ? coco_icon(icon, size: :sm) : @qualifier
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end