pulse_view_components 0.3.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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +88 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +78 -0
  5. data/app/assets/builds/js/pulse_view_components.js +9543 -0
  6. data/app/assets/builds/js/pulse_view_components.js.map +7 -0
  7. data/app/assets/builds/pulse_view_components.css +1 -0
  8. data/app/assets/config/manifest.js +2 -0
  9. data/app/assets/images/pulse_view_components/icons/alert.svg +3 -0
  10. data/app/assets/images/pulse_view_components/icons/archive.svg +1 -0
  11. data/app/assets/images/pulse_view_components/icons/arrow-both.svg +1 -0
  12. data/app/assets/images/pulse_view_components/icons/arrow-down.svg +1 -0
  13. data/app/assets/images/pulse_view_components/icons/arrow-left.svg +1 -0
  14. data/app/assets/images/pulse_view_components/icons/arrow-right.svg +1 -0
  15. data/app/assets/images/pulse_view_components/icons/arrow-switch.svg +1 -0
  16. data/app/assets/images/pulse_view_components/icons/arrow-up.svg +1 -0
  17. data/app/assets/images/pulse_view_components/icons/beaker.svg +1 -0
  18. data/app/assets/images/pulse_view_components/icons/bell-slash.svg +1 -0
  19. data/app/assets/images/pulse_view_components/icons/bell.svg +1 -0
  20. data/app/assets/images/pulse_view_components/icons/bookmark-slash.svg +1 -0
  21. data/app/assets/images/pulse_view_components/icons/bookmark.svg +1 -0
  22. data/app/assets/images/pulse_view_components/icons/calendar.svg +1 -0
  23. data/app/assets/images/pulse_view_components/icons/check-circle.svg +1 -0
  24. data/app/assets/images/pulse_view_components/icons/check.svg +1 -0
  25. data/app/assets/images/pulse_view_components/icons/chevron-down.svg +1 -0
  26. data/app/assets/images/pulse_view_components/icons/circle.svg +1 -0
  27. data/app/assets/images/pulse_view_components/icons/clock.svg +1 -0
  28. data/app/assets/images/pulse_view_components/icons/close.svg +9 -0
  29. data/app/assets/images/pulse_view_components/icons/cloud.svg +1 -0
  30. data/app/assets/images/pulse_view_components/icons/comment-discussion.svg +1 -0
  31. data/app/assets/images/pulse_view_components/icons/comment.svg +1 -0
  32. data/app/assets/images/pulse_view_components/icons/copy.svg +1 -0
  33. data/app/assets/images/pulse_view_components/icons/database.svg +1 -0
  34. data/app/assets/images/pulse_view_components/icons/dot.svg +1 -0
  35. data/app/assets/images/pulse_view_components/icons/dots-outline.svg +1 -0
  36. data/app/assets/images/pulse_view_components/icons/download.svg +1 -0
  37. data/app/assets/images/pulse_view_components/icons/duplicate.svg +1 -0
  38. data/app/assets/images/pulse_view_components/icons/eye.svg +1 -0
  39. data/app/assets/images/pulse_view_components/icons/file-added.svg +1 -0
  40. data/app/assets/images/pulse_view_components/icons/file-directory.svg +1 -0
  41. data/app/assets/images/pulse_view_components/icons/file-media.svg +1 -0
  42. data/app/assets/images/pulse_view_components/icons/file.svg +1 -0
  43. data/app/assets/images/pulse_view_components/icons/filter.svg +1 -0
  44. data/app/assets/images/pulse_view_components/icons/flame.svg +1 -0
  45. data/app/assets/images/pulse_view_components/icons/gear.svg +1 -0
  46. data/app/assets/images/pulse_view_components/icons/gift.svg +1 -0
  47. data/app/assets/images/pulse_view_components/icons/globe.svg +1 -0
  48. data/app/assets/images/pulse_view_components/icons/goal.svg +1 -0
  49. data/app/assets/images/pulse_view_components/icons/graph.svg +1 -0
  50. data/app/assets/images/pulse_view_components/icons/heart-fill.svg +1 -0
  51. data/app/assets/images/pulse_view_components/icons/heart.svg +1 -0
  52. data/app/assets/images/pulse_view_components/icons/history.svg +1 -0
  53. data/app/assets/images/pulse_view_components/icons/home.svg +1 -0
  54. data/app/assets/images/pulse_view_components/icons/hourglass.svg +1 -0
  55. data/app/assets/images/pulse_view_components/icons/image.svg +1 -0
  56. data/app/assets/images/pulse_view_components/icons/inbox.svg +1 -0
  57. data/app/assets/images/pulse_view_components/icons/info.svg +1 -0
  58. data/app/assets/images/pulse_view_components/icons/issue.svg +3 -0
  59. data/app/assets/images/pulse_view_components/icons/key.svg +1 -0
  60. data/app/assets/images/pulse_view_components/icons/law.svg +1 -0
  61. data/app/assets/images/pulse_view_components/icons/link-external.svg +1 -0
  62. data/app/assets/images/pulse_view_components/icons/link.svg +1 -0
  63. data/app/assets/images/pulse_view_components/icons/location.svg +1 -0
  64. data/app/assets/images/pulse_view_components/icons/log.svg +1 -0
  65. data/app/assets/images/pulse_view_components/icons/mail.svg +1 -0
  66. data/app/assets/images/pulse_view_components/icons/mark-github.svg +1 -0
  67. data/app/assets/images/pulse_view_components/icons/mark-jira.svg +3 -0
  68. data/app/assets/images/pulse_view_components/icons/megaphone.svg +1 -0
  69. data/app/assets/images/pulse_view_components/icons/no-entry.svg +1 -0
  70. data/app/assets/images/pulse_view_components/icons/note.svg +1 -0
  71. data/app/assets/images/pulse_view_components/icons/organization.svg +1 -0
  72. data/app/assets/images/pulse_view_components/icons/package.svg +1 -0
  73. data/app/assets/images/pulse_view_components/icons/paintbrush.svg +1 -0
  74. data/app/assets/images/pulse_view_components/icons/paperclip.svg +1 -0
  75. data/app/assets/images/pulse_view_components/icons/paste.svg +1 -0
  76. data/app/assets/images/pulse_view_components/icons/pencil.svg +1 -0
  77. data/app/assets/images/pulse_view_components/icons/people.svg +1 -0
  78. data/app/assets/images/pulse_view_components/icons/person-add.svg +1 -0
  79. data/app/assets/images/pulse_view_components/icons/person.svg +1 -0
  80. data/app/assets/images/pulse_view_components/icons/pin-slash.svg +1 -0
  81. data/app/assets/images/pulse_view_components/icons/pin.svg +1 -0
  82. data/app/assets/images/pulse_view_components/icons/play.svg +1 -0
  83. data/app/assets/images/pulse_view_components/icons/plus-circle.svg +1 -0
  84. data/app/assets/images/pulse_view_components/icons/plus.svg +1 -0
  85. data/app/assets/images/pulse_view_components/icons/project.svg +1 -0
  86. data/app/assets/images/pulse_view_components/icons/pulse.svg +1 -0
  87. data/app/assets/images/pulse_view_components/icons/question.svg +1 -0
  88. data/app/assets/images/pulse_view_components/icons/redo.svg +1 -0
  89. data/app/assets/images/pulse_view_components/icons/reply.svg +1 -0
  90. data/app/assets/images/pulse_view_components/icons/report.svg +1 -0
  91. data/app/assets/images/pulse_view_components/icons/rss.svg +1 -0
  92. data/app/assets/images/pulse_view_components/icons/ruby.svg +1 -0
  93. data/app/assets/images/pulse_view_components/icons/search.svg +1 -0
  94. data/app/assets/images/pulse_view_components/icons/share.svg +1 -0
  95. data/app/assets/images/pulse_view_components/icons/shield.svg +1 -0
  96. data/app/assets/images/pulse_view_components/icons/sliders.svg +1 -0
  97. data/app/assets/images/pulse_view_components/icons/smiley.svg +1 -0
  98. data/app/assets/images/pulse_view_components/icons/sort.svg +1 -0
  99. data/app/assets/images/pulse_view_components/icons/sort_asc.svg +1 -0
  100. data/app/assets/images/pulse_view_components/icons/sort_desc.svg +1 -0
  101. data/app/assets/images/pulse_view_components/icons/stack.svg +1 -0
  102. data/app/assets/images/pulse_view_components/icons/star-fill.svg +1 -0
  103. data/app/assets/images/pulse_view_components/icons/star.svg +1 -0
  104. data/app/assets/images/pulse_view_components/icons/stopwatch.svg +1 -0
  105. data/app/assets/images/pulse_view_components/icons/sun.svg +1 -0
  106. data/app/assets/images/pulse_view_components/icons/sync.svg +1 -0
  107. data/app/assets/images/pulse_view_components/icons/table.svg +1 -0
  108. data/app/assets/images/pulse_view_components/icons/tag.svg +1 -0
  109. data/app/assets/images/pulse_view_components/icons/tasklist.svg +1 -0
  110. data/app/assets/images/pulse_view_components/icons/telescope.svg +1 -0
  111. data/app/assets/images/pulse_view_components/icons/three-bars.svg +1 -0
  112. data/app/assets/images/pulse_view_components/icons/thumbsdown.svg +1 -0
  113. data/app/assets/images/pulse_view_components/icons/thumbsup.svg +1 -0
  114. data/app/assets/images/pulse_view_components/icons/tools.svg +1 -0
  115. data/app/assets/images/pulse_view_components/icons/trash.svg +1 -0
  116. data/app/assets/images/pulse_view_components/icons/trophy.svg +1 -0
  117. data/app/assets/images/pulse_view_components/icons/undo.svg +1 -0
  118. data/app/assets/images/pulse_view_components/icons/unlink.svg +1 -0
  119. data/app/assets/images/pulse_view_components/icons/upload.svg +1 -0
  120. data/app/assets/images/pulse_view_components/icons/x-circle.svg +1 -0
  121. data/app/assets/images/pulse_view_components/icons/x.svg +1 -0
  122. data/app/assets/images/pulse_view_components/icons/zap.svg +1 -0
  123. data/app/assets/javascripts/pulse_view_components.js +0 -0
  124. data/app/assets/stylesheets/pulse_view_components/application.tailwind.css +10 -0
  125. data/app/components/pulse/action_list/divider.rb +35 -0
  126. data/app/components/pulse/action_list/form_wrapper.html.slim +9 -0
  127. data/app/components/pulse/action_list/form_wrapper.rb +63 -0
  128. data/app/components/pulse/action_list/heading.rb +52 -0
  129. data/app/components/pulse/action_list/item.html.slim +43 -0
  130. data/app/components/pulse/action_list/item.rb +319 -0
  131. data/app/components/pulse/action_list.css +568 -0
  132. data/app/components/pulse/action_list.rb +261 -0
  133. data/app/components/pulse/badge.rb +59 -0
  134. data/app/components/pulse/base_component.rb +36 -0
  135. data/app/components/pulse/blank_slate.html.slim +10 -0
  136. data/app/components/pulse/blank_slate.rb +19 -0
  137. data/app/components/pulse/blank_slate.yml +2 -0
  138. data/app/components/pulse/border_box/header.html.slim +4 -0
  139. data/app/components/pulse/border_box/header.rb +48 -0
  140. data/app/components/pulse/border_box.html.slim +10 -0
  141. data/app/components/pulse/border_box.rb +124 -0
  142. data/app/components/pulse/box.rb +17 -0
  143. data/app/components/pulse/breadcrumb_item.rb +40 -0
  144. data/app/components/pulse/breadcrumbs.html.slim +9 -0
  145. data/app/components/pulse/breadcrumbs.rb +17 -0
  146. data/app/components/pulse/button.rb +148 -0
  147. data/app/components/pulse/component.rb +99 -0
  148. data/app/components/pulse/conditional_wrapper.rb +21 -0
  149. data/app/components/pulse/content.rb +11 -0
  150. data/app/components/pulse/counter.css +10 -0
  151. data/app/components/pulse/counter.rb +107 -0
  152. data/app/components/pulse/flash.html.slim +9 -0
  153. data/app/components/pulse/flash.rb +52 -0
  154. data/app/components/pulse/forms/error_explanation.html.slim +8 -0
  155. data/app/components/pulse/forms/error_explanation.rb +36 -0
  156. data/app/components/pulse/icon.rb +30 -0
  157. data/app/components/pulse/label.html.slim +5 -0
  158. data/app/components/pulse/label.rb +42 -0
  159. data/app/components/pulse/layout.css +239 -0
  160. data/app/components/pulse/layout.html.slim +7 -0
  161. data/app/components/pulse/layout.rb +141 -0
  162. data/app/components/pulse/link.html.slim +7 -0
  163. data/app/components/pulse/link.rb +52 -0
  164. data/app/components/pulse/modal.html.slim +51 -0
  165. data/app/components/pulse/modal.rb +64 -0
  166. data/app/components/pulse/modal_controller.js +77 -0
  167. data/app/components/pulse/nav_link.html.slim +16 -0
  168. data/app/components/pulse/nav_link.rb +25 -0
  169. data/app/components/pulse/nav_list/divider.rb +13 -0
  170. data/app/components/pulse/nav_list/group.html.slim +6 -0
  171. data/app/components/pulse/nav_list/group.rb +110 -0
  172. data/app/components/pulse/nav_list/heading.rb +36 -0
  173. data/app/components/pulse/nav_list/item.rb +171 -0
  174. data/app/components/pulse/nav_list.html.slim +11 -0
  175. data/app/components/pulse/nav_list.rb +225 -0
  176. data/app/components/pulse/popover.html.slim +5 -0
  177. data/app/components/pulse/popover.rb +88 -0
  178. data/app/components/pulse/progress_bar.html.slim +5 -0
  179. data/app/components/pulse/progress_bar.rb +23 -0
  180. data/app/components/pulse/progress_bar.yml +2 -0
  181. data/app/components/pulse/table/cell.html.slim +10 -0
  182. data/app/components/pulse/table/cell.rb +90 -0
  183. data/app/components/pulse/table/cell.yml +5 -0
  184. data/app/components/pulse/table/column.rb +22 -0
  185. data/app/components/pulse/table.html.slim +50 -0
  186. data/app/components/pulse/table.rb +111 -0
  187. data/app/components/pulse/table.yml +2 -0
  188. data/app/components/pulse/text.rb +72 -0
  189. data/app/components/pulse/toast.html.slim +14 -0
  190. data/app/components/pulse/toast.rb +85 -0
  191. data/app/components/pulse/toast_controller.js +32 -0
  192. data/app/javascript/pulse_view_components/application.js +24 -0
  193. data/app/javascript/pulse_view_components.js +4 -0
  194. data/app/lib/pulse/attributes_helper.rb +107 -0
  195. data/app/lib/pulse/fetch_or_fallback_helper.rb +53 -0
  196. data/app/lib/pulse/svg_helper.rb +28 -0
  197. data/lib/pulse/view_components/engine.rb +74 -0
  198. data/lib/pulse/view_components/version.rb +17 -0
  199. data/lib/pulse/view_components.rb +14 -0
  200. data/lib/pulse_view_components.rb +3 -0
  201. data/lib/tasks/pulse_view_components_tasks.rake +8 -0
  202. data/previews/pulse/badge_preview.rb +51 -0
  203. data/previews/pulse/blank_slate_preview.rb +14 -0
  204. data/previews/pulse/border_box_preview.rb +48 -0
  205. data/previews/pulse/breadcrumbs_preview.rb +13 -0
  206. data/previews/pulse/button_preview.rb +137 -0
  207. data/previews/pulse/counter_preview.rb +14 -0
  208. data/previews/pulse/flash_preview.rb +85 -0
  209. data/previews/pulse/icon_preview.rb +12 -0
  210. data/previews/pulse/label_preview.rb +10 -0
  211. data/previews/pulse/layout_preview.rb +34 -0
  212. data/previews/pulse/link_preview.rb +55 -0
  213. data/previews/pulse/modal_preview.rb +48 -0
  214. data/previews/pulse/nav_link_preview.rb +48 -0
  215. data/previews/pulse/nav_list_preview.rb +138 -0
  216. data/previews/pulse/popover_preview.rb +77 -0
  217. data/previews/pulse/progress_bar_preview.rb +23 -0
  218. data/previews/pulse/table_preview.rb +168 -0
  219. data/previews/pulse/text_preview.rb +102 -0
  220. data/previews/pulse/toast_preview.rb +67 -0
  221. metadata +344 -0
@@ -0,0 +1,239 @@
1
+ /* Layout */
2
+
3
+ @define-mixin flow-as-row {
4
+ grid-auto-flow: row;
5
+ grid-template-columns: 1fr !important;
6
+
7
+ & .Layout-sidebar,
8
+ & .Layout-divider,
9
+ & .Layout-main {
10
+ @apply pulse-w-full pulse-col-[1];
11
+ }
12
+
13
+ &.Layout--sidebarPosition-flowRow-start {
14
+ & .Layout-sidebar {
15
+ @apply pulse-row-[1];
16
+ }
17
+
18
+ & .Layout-main {
19
+ @apply pulse-row-[2_/_span_2];
20
+ }
21
+ }
22
+
23
+ &.Layout--sidebarPosition-flowRow-end {
24
+ & .Layout-sidebar {
25
+ @apply pulse-row-[2_/_span_2];
26
+ }
27
+
28
+ & .Layout-main {
29
+ @apply pulse-row-[1];
30
+ }
31
+ }
32
+
33
+ &.Layout--sidebarPosition-flowRow-none {
34
+ & .Layout-sidebar {
35
+ @apply pulse-hidden;
36
+ }
37
+ }
38
+
39
+ &.Layout--divided {
40
+ @mixin flow-as-row-divider;
41
+
42
+ & .Layout-main {
43
+ @apply pulse-row-[3_/_span_1];
44
+ }
45
+
46
+ &.Layout--sidebarPosition-flowRow-end {
47
+ & .Layout-sidebar {
48
+ @apply pulse-row-[3_/_span_1];
49
+ }
50
+
51
+ & .Layout-main {
52
+ @apply pulse-row-[1];
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ @define-mixin flow-as-row-divider {
59
+ --Layout-gutter: 0;
60
+
61
+ & .Layout-divider {
62
+ @apply pulse-h-px pulse-row-[2];
63
+
64
+ &.Layout-divider--flowRow-hidden {
65
+ @apply pulse-hidden;
66
+ }
67
+
68
+ &.Layout-divider--flowRow-shallow {
69
+ @apply pulse-h-2 pulse-border-[color:var(--borderColor-default)] pulse-border-[var(--borderWidth-thin)_0] pulse-mr-0 pulse-border-solid;
70
+ background: var(--bgColor-inset);
71
+ }
72
+ }
73
+ }
74
+
75
+ .Layout {
76
+ @apply pulse-grid pulse-grid-flow-col pulse-grid-cols-[auto_0_minmax(0,calc(100%_-_var(--Layout-sidebar-width)_-_var(--Layout-gutter)))] pulse-gap-[var(--Layout-gutter)];
77
+ --Layout-sidebar-width: 220px;
78
+ --Layout-gutter: 16px;
79
+ }
80
+ @media (max-width: calc(544px - 0.02px)) {
81
+ .Layout {
82
+ @mixin flow-as-row;
83
+ }
84
+ }
85
+ @media (max-width: calc(768px - 0.02px)) {
86
+ .Layout.Layout--flowRow-until-md {
87
+ @mixin flow-as-row;
88
+ }
89
+ }
90
+ @media (max-width: calc(1012px - 0.02px)) {
91
+ .Layout.Layout--flowRow-until-lg {
92
+ @mixin flow-as-row;
93
+ }
94
+ }
95
+
96
+ /* Flow as column */
97
+
98
+ /* sidebar column, separator, main column */
99
+ .Layout .Layout-sidebar {
100
+ @apply pulse-col-[1];
101
+ }
102
+ .Layout .Layout-divider {
103
+ @apply pulse-hidden;
104
+ }
105
+ .Layout .Layout-main {
106
+ @apply pulse-col-[2_/_span_2];
107
+ }
108
+
109
+ /* Gutter spacing */
110
+ @media (min-width: 1012px) {
111
+ .Layout {
112
+ --Layout-gutter: 24px;
113
+ }
114
+ }
115
+ .Layout.Layout--gutter-none {
116
+ /* stylelint-disable-next-line length-zero-no-unit */
117
+ --Layout-gutter: 0px;
118
+
119
+ /* Neds px in value */
120
+ }
121
+ .Layout.Layout--gutter-condensed {
122
+ --Layout-gutter: 16px;
123
+ }
124
+ @media (min-width: 1012px) {
125
+ .Layout.Layout--gutter-spacious {
126
+ --Layout-gutter: 32px;
127
+ }
128
+ }
129
+ @media (min-width: 1280px) {
130
+ .Layout.Layout--gutter-spacious {
131
+ --Layout-gutter: 40px;
132
+ }
133
+ }
134
+
135
+ /* Sidebar width */
136
+ @media (min-width: 544px) {
137
+ .Layout {
138
+ --Layout-sidebar-width: 220px;
139
+ }
140
+ }
141
+ @media (min-width: 768px) {
142
+ .Layout {
143
+ --Layout-sidebar-width: 256px;
144
+ }
145
+ }
146
+ @media (min-width: 1012px) {
147
+ .Layout {
148
+ --Layout-sidebar-width: 296px;
149
+ }
150
+ }
151
+ @media (min-width: 768px) {
152
+ .Layout.Layout--sidebar-narrow {
153
+ --Layout-sidebar-width: 240px;
154
+ }
155
+ }
156
+ @media (min-width: 1012px) {
157
+ .Layout.Layout--sidebar-narrow {
158
+ --Layout-sidebar-width: 256px;
159
+ }
160
+ }
161
+ @media (min-width: 1012px) {
162
+ .Layout.Layout--sidebar-wide {
163
+ --Layout-sidebar-width: 320px;
164
+ }
165
+ }
166
+ @media (min-width: 1280px) {
167
+ .Layout.Layout--sidebar-wide {
168
+ --Layout-sidebar-width: 336px;
169
+ }
170
+ }
171
+
172
+ /* Sidebar position */
173
+ .Layout.Layout--sidebarPosition-start .Layout-sidebar {
174
+ @apply pulse-col-[1];
175
+ }
176
+ .Layout.Layout--sidebarPosition-start .Layout-main {
177
+ @apply pulse-col-[2_/_span_2];
178
+ }
179
+ .Layout.Layout--sidebarPosition-end {
180
+ @apply pulse-grid-cols-[minmax(0,calc(100%_-_var(--Layout-sidebar-width)_-_var(--Layout-gutter)))_0_auto];
181
+ }
182
+ .Layout.Layout--sidebarPosition-end .Layout-main {
183
+ @apply pulse-col-[1];
184
+ }
185
+ .Layout.Layout--sidebarPosition-end .Layout-sidebar {
186
+ @apply pulse-col-[2_/_span_2];
187
+ }
188
+
189
+ /* Sidebar divider */
190
+ .Layout.Layout--divided .Layout-divider {
191
+ @apply pulse-block pulse-col-[2] pulse-w-px -pulse-mr-px;
192
+ background: var(--borderColor-default);
193
+ }
194
+ .Layout.Layout--divided .Layout-main {
195
+ @apply pulse-col-[3_/_span_1];
196
+ }
197
+ .Layout.Layout--divided.Layout--sidebarPosition-end .Layout-sidebar {
198
+ @apply pulse-col-[3_/_span_1];
199
+ }
200
+ .Layout.Layout--divided.Layout--sidebarPosition-end .Layout-main {
201
+ @apply pulse-col-[1];
202
+ }
203
+ .Layout-divider {
204
+ @apply pulse-hidden pulse-w-px;
205
+ }
206
+ .Layout-sidebar {
207
+ @apply pulse-w-[var(--Layout-sidebar-width)];
208
+ }
209
+ .Layout-main {
210
+ @apply pulse-min-w-0;
211
+ }
212
+
213
+ /* Centered main column
214
+ ** FIXME: right-aligned sidebar */
215
+ .Layout-main .Layout-main-centered-md,
216
+ .Layout-main .Layout-main-centered-lg,
217
+ .Layout-main .Layout-main-centered-xl {
218
+ @apply pulse-mx-auto;
219
+ }
220
+ .Layout-main .Layout-main-centered-md > .container-md,
221
+ .Layout-main .Layout-main-centered-md > .container-lg,
222
+ .Layout-main .Layout-main-centered-md > .container-xl,
223
+ .Layout-main .Layout-main-centered-lg > .container-md,
224
+ .Layout-main .Layout-main-centered-lg > .container-lg,
225
+ .Layout-main .Layout-main-centered-lg > .container-xl,
226
+ .Layout-main .Layout-main-centered-xl > .container-md,
227
+ .Layout-main .Layout-main-centered-xl > .container-lg,
228
+ .Layout-main .Layout-main-centered-xl > .container-xl {
229
+ @apply pulse-ml-0;
230
+ }
231
+ .Layout-main .Layout-main-centered-md {
232
+ @apply pulse-max-w-[calc(var(--breakpoint-medium)_+_var(--Layout-sidebar-width)_+_var(--Layout-gutter))];
233
+ }
234
+ .Layout-main .Layout-main-centered-lg {
235
+ @apply pulse-max-w-[calc(var(--breakpoint-large)_+_var(--Layout-sidebar-width)_+_var(--Layout-gutter))];
236
+ }
237
+ .Layout-main .Layout-main-centered-xl {
238
+ @apply pulse-max-w-[calc(var(--breakpoint-xlarge)_+_var(--Layout-sidebar-width)_+_var(--Layout-gutter))];
239
+ }
@@ -0,0 +1,7 @@
1
+ /! components/pulse/layout
2
+ = render Pulse::BaseComponent.new(**@system_arguments) do
3
+ - if @first_in_source == :main
4
+ = main
5
+ = sidebar
6
+ - if @first_in_source == :sidebar
7
+ = main
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pulse
4
+ # Renders a two-column page layout
5
+ class Layout < Pulse::Component
6
+ FIRST_IN_SOURCE_DEFAULT = :sidebar
7
+ FIRST_IN_SOURCE_OPTIONS = [FIRST_IN_SOURCE_DEFAULT, :main].freeze
8
+
9
+ GUTTER_DEFAULT = :default
10
+ GUTTER_MAPPINGS = {
11
+ # :none => "Layout--gutter-none",
12
+ # :condensed => "Layout--gutter-condensed",
13
+ # :spacious => "Layout--gutter-spacious",
14
+ GUTTER_DEFAULT => ''
15
+ }.freeze
16
+ GUTTER_OPTIONS = GUTTER_MAPPINGS.keys.freeze
17
+
18
+ SIDEBAR_WIDTH_DEFAULT = :default
19
+ SIDEBAR_WIDTH_MAPPINGS = {
20
+ SIDEBAR_WIDTH_DEFAULT => ''
21
+ # :narrow => "Layout--sidebar-narrow",
22
+ # :wide => "Layout--sidebar-wide"
23
+ }.freeze
24
+ SIDEBAR_WIDTH_OPTIONS = SIDEBAR_WIDTH_MAPPINGS.keys.freeze
25
+
26
+ # The layout's main content.
27
+ #
28
+ # @param width [Symbol] <%= one_of(Layout::Main::WIDTH_OPTIONS) %>
29
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
30
+ renders_one :main, 'Pulse::Layout::Main'
31
+
32
+ # The layout's sidebar.
33
+ #
34
+ # @param width [Symbol] <%= one_of(Layout::SIDEBAR_WIDTH_OPTIONS) %>
35
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
36
+ renders_one :sidebar, lambda { |
37
+ width: SIDEBAR_WIDTH_DEFAULT,
38
+ **system_arguments
39
+ |
40
+ # These classes have to be set in the parent `Layout` element, so we modify its system arguments.
41
+ @system_arguments[:classes] = merge_classes(
42
+ @system_arguments[:classes],
43
+ SIDEBAR_WIDTH_MAPPINGS[fetch_or_fallback(SIDEBAR_WIDTH_OPTIONS, width,
44
+ SIDEBAR_WIDTH_DEFAULT)]
45
+ )
46
+
47
+ Pulse::Layout::Sidebar.new(**system_arguments)
48
+ }
49
+
50
+ # @param first_in_source [Symbol] Which element to render first in the HTML. This will change the keyboard navigation order. <%= one_of(Layout::FIRST_IN_SOURCE_OPTIONS) %>
51
+ # @param gutter [Symbol] The amount of space between the main section and the sidebar. <%= one_of(Layout::GUTTER_OPTIONS) %>
52
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
53
+ def initialize(first_in_source: FIRST_IN_SOURCE_DEFAULT, gutter: :default,
54
+ **system_arguments)
55
+ @first_in_source = fetch_or_fallback(
56
+ FIRST_IN_SOURCE_OPTIONS,
57
+ first_in_source,
58
+ FIRST_IN_SOURCE_OPTIONS
59
+ )
60
+
61
+ @system_arguments = system_arguments
62
+ @system_arguments[:tag] = :div
63
+ @system_arguments[:classes] = merge_classes(
64
+ 'pulse-grid pulse-grid-flow-col',
65
+ 'pulse-grid-cols-[auto_0_minmax(0,calc(100%_-_var(--pulse-layout' \
66
+ '-sidebar-width)_-_var(--pulse-layout-gutter)))]',
67
+ 'pulse-gap-[var(--pulse-layout-gutter)]',
68
+ GUTTER_MAPPINGS[fetch_or_fallback(GUTTER_OPTIONS, gutter,
69
+ GUTTER_DEFAULT)],
70
+ system_arguments[:classes]
71
+ )
72
+ end
73
+
74
+ def render?
75
+ main? && sidebar?
76
+ end
77
+
78
+ # The layout's main content.
79
+ class Main < Pulse::Component
80
+ WIDTH_DEFAULT = :full
81
+ WIDTH_OPTIONS = [WIDTH_DEFAULT, :md, :lg, :xl].freeze
82
+
83
+ TAG_DEFAULT = :div
84
+ TAG_OPTIONS = [TAG_DEFAULT, :main].freeze
85
+
86
+ # @param width [Symbol] <%= one_of(Layout::Main::WIDTH_OPTIONS) %>
87
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
88
+ def initialize(tag: TAG_DEFAULT, width: WIDTH_DEFAULT, **system_arguments)
89
+ @width = fetch_or_fallback(WIDTH_OPTIONS, width, WIDTH_DEFAULT)
90
+
91
+ @system_arguments = system_arguments
92
+ @system_arguments[:tag] =
93
+ fetch_or_fallback(TAG_OPTIONS, tag, TAG_DEFAULT)
94
+ @system_arguments[:classes] = merge_classes(
95
+ 'pulse-col-[2_/_span_2]',
96
+ system_arguments[:classes]
97
+ )
98
+ end
99
+
100
+ def call
101
+ render(Pulse::BaseComponent.new(**@system_arguments)) do
102
+ if @width == :full
103
+ content
104
+ else
105
+ render(
106
+ Pulse::BaseComponent.new(
107
+ tag: :div,
108
+ classes: "pulse-mx-auto Layout-main-centered-#{@width}"
109
+ )
110
+ ) do
111
+ render(Pulse::BaseComponent.new(tag: :div, container: @width)) do
112
+ content
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ # The layout's sidebar content.
121
+ class Sidebar < Pulse::Component
122
+ TAG_DEFAULT = :div
123
+ TAG_OPTIONS = [TAG_DEFAULT, :aside, :nav, :section].freeze
124
+
125
+ def initialize(tag: TAG_DEFAULT, **system_arguments)
126
+ @system_arguments = system_arguments
127
+
128
+ @system_arguments[:tag] =
129
+ fetch_or_fallback(TAG_OPTIONS, tag, TAG_DEFAULT)
130
+ @system_arguments[:classes] = merge_classes(
131
+ 'pulse-col-[1]',
132
+ @system_arguments[:classes]
133
+ )
134
+ end
135
+
136
+ def call
137
+ render(Pulse::BaseComponent.new(**@system_arguments)) { content }
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,7 @@
1
+ /! components/pulse/link
2
+ = render Pulse::BaseComponent.new(**@options) do
3
+ // TODO: is there a way to check existence of icon file?? Or at least not
4
+ // bork the layout if it is missing
5
+ - if leading_visual
6
+ = leading_visual
7
+ = content
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pulse
4
+ # Use `Link` for navigating from one page to another.
5
+ # `Link` styles anchor tags with default styling and hover
6
+ # text-decoration.
7
+ class Link < Pulse::Component
8
+ DEFAULT_SCHEME = :tertiary
9
+ SCHEME_MAPPINGS = {
10
+ primary: 'pulse-text-primary hover:pulse-text-primary',
11
+ secondary: 'pulse-text-secondary hover:pulse-text-secondary',
12
+ tertiary: 'pulse-text-tertiary hover:pulse-text-tertiary',
13
+ reversed: 'pulse-text-gray-300 hover:pulse-text-gray-300 ' \
14
+ 'focus:pulse-text-gray-300'
15
+ }.freeze
16
+ SCHEME_OPTIONS = SCHEME_MAPPINGS.keys.freeze
17
+
18
+ renders_one :leading_visual, types: {
19
+ icon: lambda { |**options|
20
+ Pulse::Icon.new(**options)
21
+ }
22
+ }
23
+
24
+ def initialize(href: nil, scheme: DEFAULT_SCHEME, bold: false,
25
+ underline: true, condition: true, **options)
26
+ @condition = condition
27
+ @options = options
28
+ @options[:tag] = condition ? :a : :span
29
+ @options[:href] = href if condition
30
+ @options[:classes] = merge_classes(
31
+ SCHEME_MAPPINGS[fetch_or_fallback(SCHEME_MAPPINGS.keys, scheme,
32
+ DEFAULT_SCHEME)],
33
+ @options[:classes],
34
+ 'pulse-font-bold': bold,
35
+ 'pulse-underline hover:pulse-no-underline': condition && underline
36
+ )
37
+ end
38
+
39
+ def before_render
40
+ if @options[:href].nil? && @condition && !Rails.env.production?
41
+ raise ArgumentError, 'href is required'
42
+ end
43
+
44
+ return unless leading_visual
45
+
46
+ @options[:classes] = class_names(
47
+ @options[:classes],
48
+ 'pulse-flex pulse-items-center pulse-gap-1.5'
49
+ )
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,51 @@
1
+ = turbo_frame_tag 'pulse_modal' do
2
+ = render Pulse::BaseComponent.new(**@system_arguments) do
3
+ - unless auto_close
4
+ div class="pulse-flex pulse-items-center pulse-justify-center pulse-min-h-screen pulse-p-6 pulse-box-border"
5
+ /! bg overlay
6
+ div{
7
+ data-pulse--modal-target="wrapper" data-action="#{close_on_overlay_click ? 'click->pulse--modal#close' : ''}" aria-hidden="true"
8
+ data-transition-enter="pulse-ease-out pulse-duration-300"
9
+ data-transition-enter-start="pulse-opacity-0"
10
+ data-transition-enter-end="pulse-opacity-100"
11
+ data-transition-leave="pulse-ease-in pulse-duration-200"
12
+ data-transition-leave-start="pulse-opacity-100"
13
+ data-transition-leave-end="pulse-opacity-0"
14
+ class="pulse-fixed pulse-inset-0 pulse-bg-neutral-700/75 pulse-transition-opacity" }
15
+
16
+ /! modal
17
+ div{
18
+ data-pulse--modal-target="body"
19
+ data-transition-enter="pulse-ease-out pulse-duration-300"
20
+ data-transition-enter-start="pulse-opacity-0 pulse-translate-y-4 sm:pulse-translate-y-0 sm:pulse-scale-95"
21
+ data-transition-enter-end="pulse-opacity-100 pulse-translate-y-0 sm:pulse-scale-100"
22
+ data-transition-leave="pulse-ease-in pulse-duration-200"
23
+ data-transition-leave-start="pulse-opacity-100 pulse-translate-y-0 sm:pulse-scale-100"
24
+ data-transition-leave-end="pulse-opacity-0 pulse-translate-y-4 sm:pulse-translate-y-0 sm:pulse-scale-95"
25
+ class="pulse-relative pulse-z-50 pulse-bg-white pulse-shadow-xl pulse-rounded-lg pulse-transition-all #{modal_classes}"
26
+ role="dialog"
27
+ aria-modal="true"}
28
+
29
+ /! header
30
+ div class="pulse-flex pulse-items-center pulse-justify-between pulse-gap-4 pulse-p-4 md:pulse-p-5 pulse-border-b"
31
+ - if title?
32
+ = render Pulse::Text.new(type: :h4, el_tag: :h2, classes: 'pulse-tracking-tight pulse-text-gray pulse-truncate')
33
+ = title
34
+
35
+ - if close_button
36
+ button{
37
+ class="pulse-ms-auto pulse-inline-flex pulse-items-center pulse-justify-center pulse-w-10 pulse-h-7 pulse-bg-transparent pulse-border-none pulse-text-gray"
38
+ data-action="pulse--modal#close"
39
+ aria-label="Close Modal"}
40
+
41
+ = render Pulse::Icon.new(icon: :close, width: 16, height: 16)
42
+
43
+
44
+ /! body
45
+ div class="pulse-p-4 md:pulse-p-5 #{'pulse-mb-4' unless footer}"
46
+ = content
47
+
48
+ /! footer
49
+ - if footer
50
+ div class="pulse-flex pulse-items-center pulse-p-4 md:pulse-p-5 pulse-border-t pulse-border-gray-300"
51
+ = footer
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pulse
4
+ # Renders a modal component targetable by turbo frame
5
+ class Modal < Pulse::Component
6
+ include Turbo::FramesHelper
7
+
8
+ renders_one :title
9
+ renders_one :footer
10
+
11
+ DEFAULT_SIZE = :md
12
+ SIZE_MAPPINGS = {
13
+ sm: 'pulse-max-w-md',
14
+ md: 'pulse-max-w-2xl',
15
+ lg: 'pulse-max-w-4xl',
16
+ xl: 'pulse-max-w-5xl',
17
+ xxl: 'pulse-max-w-6xl'
18
+ }.freeze
19
+ SIZE_OPTIONS = SIZE_MAPPINGS.keys.freeze
20
+
21
+ def initialize(size: DEFAULT_SIZE, close_button: true,
22
+ close_on_esc: true, close_on_overlay_click: true,
23
+ close_on_submit: true, auto_close: false, **system_arguments)
24
+ @size = fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)
25
+ @system_arguments = system_arguments
26
+ @modal_classes = merge_classes(
27
+ 'pulse-w-full',
28
+ SIZE_MAPPINGS[@size],
29
+ @system_arguments.delete(:classes)
30
+ )
31
+ @close_button = close_button
32
+ @close_on_esc = close_on_esc
33
+ @close_on_overlay_click = close_on_overlay_click
34
+ @close_on_submit = close_on_submit
35
+ @auto_close = auto_close
36
+
37
+ @system_arguments[:'aria-labelledby'] = 'modal-title'
38
+ @system_arguments[:'aria-modal'] = true
39
+ @system_arguments[:classes] = 'pulse-fixed pulse-z-9999 pulse-inset-0 ' \
40
+ 'pulse-overflow-y-auto'
41
+ @system_arguments[:data] = data_attributes
42
+ @system_arguments[:role] = 'dialog'
43
+ @system_arguments[:tabindex] = 0
44
+ @system_arguments[:tag] = :div
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :modal_classes, :close_button, :close_on_esc,
50
+ :close_on_overlay_click, :close_on_submit, :auto_close
51
+
52
+ def data_attributes
53
+ merge_attributes(
54
+ @system_arguments[:data],
55
+ {
56
+ controller: 'pulse--modal',
57
+ 'pulse--modal-close-on-esc-value': close_on_esc,
58
+ 'pulse--modal-close-on-submit-value': close_on_submit,
59
+ 'pulse--modal-auto-close-value': auto_close
60
+ }
61
+ )
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,77 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { enter, leave } from "el-transition"
3
+
4
+ // Connects to data-controller="pulse--modal"
5
+ export default class extends Controller {
6
+ static targets = ["wrapper", "body"]
7
+ static values = {
8
+ closeOnEsc: { type: Boolean, default: true },
9
+ closeOnSubmit: { type: Boolean, default: true },
10
+ autoClose: { type: Boolean, default: false },
11
+ }
12
+
13
+ connect() {
14
+ if (!this.autoCloseValue) {
15
+ document.addEventListener("keydown", this.handleKeyDown)
16
+
17
+ if (this.closeOnSubmitValue) {
18
+ document.addEventListener("turbo:submit-end", this.handleSubmit)
19
+ }
20
+ this.lastFocusedElement = null
21
+ this.open()
22
+ } else {
23
+ this.autoClose()
24
+ }
25
+ }
26
+
27
+ disconnect() {
28
+ document.removeEventListener("keydown", this.handleKeyDown)
29
+
30
+ if (this.closeOnSubmitValue) {
31
+ document.removeEventListener("turbo:submit-end", this.handleSubmit)
32
+ }
33
+ }
34
+
35
+ handleKeyDown = e => {
36
+ if (this.closeOnEscValue && e.key === "Escape") {
37
+ e.stopPropagation()
38
+ this.close()
39
+ }
40
+ }
41
+
42
+ open() {
43
+ this.lastFocusedElement = document.activeElement
44
+ enter(this.wrapperTarget)
45
+ enter(this.bodyTarget)
46
+ document.body.classList.add("pulse-overflow-hidden")
47
+ }
48
+
49
+ close(e) {
50
+ leave(this.wrapperTarget)
51
+ leave(this.bodyTarget).then(() => {
52
+ // Remove the modal element after the fade out so it doesn't blanket the
53
+ // screen
54
+ this.element.remove()
55
+ })
56
+
57
+ document.body.classList.remove("pulse-overflow-hidden")
58
+
59
+ // Return focus to the last focused element
60
+ if (this.lastFocusedElement) this.lastFocusedElement.focus()
61
+
62
+ // Remove src reference from parent frame element
63
+ // Without this, turbo won't re-open the modal on subsequent clicks
64
+ this.element.closest("turbo-frame").src = undefined
65
+ }
66
+
67
+ autoClose(e) {
68
+ this.element.remove()
69
+ document.body.classList.remove("pulse-overflow-hidden")
70
+ }
71
+
72
+ handleSubmit = e => {
73
+ if (e.detail.success) {
74
+ this.close()
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,16 @@
1
+ /! components/pulse/nav_link
2
+ = render Pulse::BaseComponent.new(**@system_arguments) do
3
+ - if @variant == :desktop
4
+ = active_link_to @path, active: @active_mode,
5
+ class_active: 'pulse-text-white hover:pulse-text-white hover:pulse-no-underline pulse-px-3 pulse-py-2 pulse-font-medium pulse-relative pulse-group pulse-is-active',
6
+ class_inactive: 'pulse-text-white hover:pulse-text-white hover:pulse-no-underline pulse-px-3 pulse-py-2 pulse-font-medium pulse-relative pulse-group' do
7
+ = @label
8
+ span class="pulse-absolute -pulse-bottom-0.5 pulse-left-1/2 pulse-w-0 pulse-h-0.5 pulse-bg-gray-300 group-hover:pulse-w-1/2 group-hover:pulse-transition-all pulse-duration-200"
9
+ span class="pulse-absolute -pulse-bottom-0.5 pulse-right-1/2 pulse-w-0 pulse-h-0.5 pulse-bg-gray-300 group-hover:pulse-w-1/2 group-hover:pulse-transition-all pulse-duration-200"
10
+ span class="pulse-absolute -pulse-bottom-0.5 pulse-start-0 pulse-w-full pulse-h-0.5 pulse-bg-primary pulse-hidden group-[.pulse-is-active]:pulse-block"
11
+ - else
12
+ = active_link_to @path, active: @active_mode,
13
+ class_active: 'pulse-text-primary-800 hover:pulse-no-underline pulse-block pulse-px-3 pulse-py-2',
14
+ class_inactive: 'pulse-text-gray hover:pulse-no-underline pulse-block pulse-px-3 pulse-py-2',
15
+ data: { action: 'dropdown#toggle' } do
16
+ = @label
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pulse
4
+ # Links in navbars
5
+ class NavLink < Pulse::Component
6
+ DEFAULT_VARIANT = :desktop
7
+ VARIANT_MAPPINGS = {
8
+ desktop: 'pulse-px-3 pulse-py-2',
9
+ mobile: 'pulse-px-3 pulse-py-2'
10
+ }.freeze
11
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys.freeze
12
+
13
+ delegate :active_link_to, to: :helpers
14
+
15
+ def initialize(label:, path:, active_mode: :inclusive, variant: :desktop,
16
+ **system_arguments)
17
+ @label = label
18
+ @path = path
19
+ @active_mode = active_mode
20
+ @variant = variant
21
+ @system_arguments = system_arguments
22
+ @system_arguments[:tag] ||= :div
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pulse
4
+ class NavList
5
+ # Separator with optional text rendered above groups or between individual
6
+ # items.
7
+ class Divider < Pulse::ActionList::Divider
8
+ def kind
9
+ :divider
10
+ end
11
+ end
12
+ end
13
+ end