neo_components 0.1.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 (223) hide show
  1. checksums.yaml +7 -0
  2. data/app/assets/icons/academy.svg +3 -0
  3. data/app/assets/icons/adjustments-horizontal.svg +3 -0
  4. data/app/assets/icons/adjustments-vertical.svg +3 -0
  5. data/app/assets/icons/arrow-down-tray.svg +3 -0
  6. data/app/assets/icons/arrow-left.svg +3 -0
  7. data/app/assets/icons/arrow-long-down.svg +3 -0
  8. data/app/assets/icons/arrow-long-right.svg +3 -0
  9. data/app/assets/icons/arrow-long-up.svg +3 -0
  10. data/app/assets/icons/arrow-right.svg +3 -0
  11. data/app/assets/icons/arrow-up-tray.svg +3 -0
  12. data/app/assets/icons/arrow-uturn-left.svg +3 -0
  13. data/app/assets/icons/arrow-uturn-right.svg +3 -0
  14. data/app/assets/icons/assign-user.svg +13 -0
  15. data/app/assets/icons/at-symbol.svg +3 -0
  16. data/app/assets/icons/bars-4.svg +3 -0
  17. data/app/assets/icons/bell.svg +3 -0
  18. data/app/assets/icons/book.svg +3 -0
  19. data/app/assets/icons/bookmark.svg +3 -0
  20. data/app/assets/icons/building-office-2.svg +3 -0
  21. data/app/assets/icons/calendar.svg +3 -0
  22. data/app/assets/icons/celebration.svg +15 -0
  23. data/app/assets/icons/check-badge-solid.svg +3 -0
  24. data/app/assets/icons/check-circle-solid.svg +3 -0
  25. data/app/assets/icons/check-circle.svg +3 -0
  26. data/app/assets/icons/check-list.svg +12 -0
  27. data/app/assets/icons/check.svg +3 -0
  28. data/app/assets/icons/chevron-double-right.svg +3 -0
  29. data/app/assets/icons/chevron-down.svg +3 -0
  30. data/app/assets/icons/chevron-left.svg +3 -0
  31. data/app/assets/icons/chevron-right.svg +3 -0
  32. data/app/assets/icons/chevron-up.svg +3 -0
  33. data/app/assets/icons/circle-stack.svg +3 -0
  34. data/app/assets/icons/clipboard-document-check-solid.svg +4 -0
  35. data/app/assets/icons/clipboard-document-check.svg +3 -0
  36. data/app/assets/icons/clock.svg +3 -0
  37. data/app/assets/icons/complete.svg +3 -0
  38. data/app/assets/icons/course.svg +3 -0
  39. data/app/assets/icons/device-phone-mobile.svg +3 -0
  40. data/app/assets/icons/document-arrow-up.svg +4 -0
  41. data/app/assets/icons/document-text.svg +4 -0
  42. data/app/assets/icons/ellipsis-vertical.svg +3 -0
  43. data/app/assets/icons/exclaimation-circle-solid.svg +3 -0
  44. data/app/assets/icons/eye.svg +4 -0
  45. data/app/assets/icons/flag.svg +3 -0
  46. data/app/assets/icons/funnel.svg +3 -0
  47. data/app/assets/icons/green-circle.svg +3 -0
  48. data/app/assets/icons/green-tick.svg +3 -0
  49. data/app/assets/icons/grid.svg +6 -0
  50. data/app/assets/icons/home.svg +3 -0
  51. data/app/assets/icons/i-vector.svg +3 -0
  52. data/app/assets/icons/lessons.svg +11 -0
  53. data/app/assets/icons/magnifying-glass.svg +3 -0
  54. data/app/assets/icons/minus.svg +3 -0
  55. data/app/assets/icons/numbered-list.svg +3 -0
  56. data/app/assets/icons/pencil.svg +3 -0
  57. data/app/assets/icons/play.svg +3 -0
  58. data/app/assets/icons/plus.svg +3 -0
  59. data/app/assets/icons/power.svg +3 -0
  60. data/app/assets/icons/question-mark-circle.svg +3 -0
  61. data/app/assets/icons/quiz-score.svg +15 -0
  62. data/app/assets/icons/re-invite.svg +3 -0
  63. data/app/assets/icons/red-circle.svg +3 -0
  64. data/app/assets/icons/share.svg +3 -0
  65. data/app/assets/icons/smiley-five.svg +5 -0
  66. data/app/assets/icons/smiley-four.svg +5 -0
  67. data/app/assets/icons/smiley-one.svg +5 -0
  68. data/app/assets/icons/smiley-three.svg +5 -0
  69. data/app/assets/icons/smiley-two.svg +5 -0
  70. data/app/assets/icons/sparkle.svg +3 -0
  71. data/app/assets/icons/star-transparent.svg +3 -0
  72. data/app/assets/icons/stop.svg +4 -0
  73. data/app/assets/icons/support-placeholder.svg +4 -0
  74. data/app/assets/icons/tag.svg +4 -0
  75. data/app/assets/icons/timer.svg +5 -0
  76. data/app/assets/icons/transcript.svg +31 -0
  77. data/app/assets/icons/trash.svg +3 -0
  78. data/app/assets/icons/upload-info.svg +7 -0
  79. data/app/assets/icons/user-circle.svg +3 -0
  80. data/app/assets/icons/user-group.svg +3 -0
  81. data/app/assets/icons/user.svg +3 -0
  82. data/app/assets/icons/users.svg +3 -0
  83. data/app/assets/icons/winner.svg +35 -0
  84. data/app/assets/icons/x-circle-solid.svg +3 -0
  85. data/app/assets/icons/x-circle.svg +3 -0
  86. data/app/assets/icons/x-mark.svg +3 -0
  87. data/app/assets/stylesheets/breadcrumbs.tailwind.css +45 -0
  88. data/app/assets/stylesheets/buttons.tailwind.css +98 -0
  89. data/app/assets/stylesheets/course_progress.css +75 -0
  90. data/app/assets/stylesheets/custom.css +67 -0
  91. data/app/assets/stylesheets/date_picker.tailwind.css +7 -0
  92. data/app/assets/stylesheets/directives.tailwind.css +3 -0
  93. data/app/assets/stylesheets/dropdown.tailwind.css +11 -0
  94. data/app/assets/stylesheets/file_selector.tailwind.css +47 -0
  95. data/app/assets/stylesheets/icons.css.erb +244 -0
  96. data/app/assets/stylesheets/inputs.tailwind.css +53 -0
  97. data/app/assets/stylesheets/menu_component.tailwind.css +39 -0
  98. data/app/assets/stylesheets/mobile_inputs.tailwind.css +11 -0
  99. data/app/assets/stylesheets/modalbox.tailwind.css +35 -0
  100. data/app/assets/stylesheets/profile_icon.tailwind.css +15 -0
  101. data/app/assets/stylesheets/textarea.tailwind.css +43 -0
  102. data/app/assets/stylesheets/tooltip.css +88 -0
  103. data/app/assets/stylesheets/typography.tailwind.css +70 -0
  104. data/app/helpers/ui_helper.rb +31 -0
  105. data/app/helpers/view_component/accordion_component.rb +12 -0
  106. data/app/helpers/view_component/breadcrumbs_component.rb +30 -0
  107. data/app/helpers/view_component/button_component.rb +180 -0
  108. data/app/helpers/view_component/chip_component.rb +54 -0
  109. data/app/helpers/view_component/component_helper.rb +21 -0
  110. data/app/helpers/view_component/course_card_component.rb +9 -0
  111. data/app/helpers/view_component/course_carousal_component.rb +14 -0
  112. data/app/helpers/view_component/course_select_component.rb +32 -0
  113. data/app/helpers/view_component/doc_section_component.rb +10 -0
  114. data/app/helpers/view_component/icon_component.rb +24 -0
  115. data/app/helpers/view_component/input_component/date_picker_component.rb +127 -0
  116. data/app/helpers/view_component/input_component/dropdown_component.rb +99 -0
  117. data/app/helpers/view_component/input_component/file_selector_component.rb +123 -0
  118. data/app/helpers/view_component/input_component/input_checkbox_component.rb +54 -0
  119. data/app/helpers/view_component/input_component/input_mobile_component.rb +68 -0
  120. data/app/helpers/view_component/input_component/input_radio_component.rb +54 -0
  121. data/app/helpers/view_component/input_component/input_text_component.rb +137 -0
  122. data/app/helpers/view_component/input_component/textarea_component.rb +101 -0
  123. data/app/helpers/view_component/input_component.rb +290 -0
  124. data/app/helpers/view_component/input_textarea_component.rb +28 -0
  125. data/app/helpers/view_component/long_course_card_component.rb +10 -0
  126. data/app/helpers/view_component/member_list_component.rb +17 -0
  127. data/app/helpers/view_component/menu_component.rb +31 -0
  128. data/app/helpers/view_component/menu_component_helper.rb +22 -0
  129. data/app/helpers/view_component/menu_item.rb +12 -0
  130. data/app/helpers/view_component/modal_box_component.rb +29 -0
  131. data/app/helpers/view_component/modal_component.rb +12 -0
  132. data/app/helpers/view_component/notification_bar_component.rb +22 -0
  133. data/app/helpers/view_component/paginator_component.rb +9 -0
  134. data/app/helpers/view_component/profile_icon_component.rb +46 -0
  135. data/app/helpers/view_component/progress_component.rb +12 -0
  136. data/app/helpers/view_component/table_component.rb +22 -0
  137. data/app/helpers/view_component/typography_component.rb +83 -0
  138. data/app/javascript/neo_components/controllers/collapsible_controller.js +37 -0
  139. data/app/javascript/neo_components/controllers/date_picker_controller.js +17 -0
  140. data/app/javascript/neo_components/controllers/file_selector_controller.js +145 -0
  141. data/app/javascript/neo_components/controllers/input_mobile_controller.js +7 -0
  142. data/app/javascript/neo_components/controllers/menu_component_controller.js +26 -0
  143. data/app/javascript/neo_components/controllers/modal_loader_controller.js +13 -0
  144. data/app/javascript/neo_components/controllers/modals_controller.js +26 -0
  145. data/app/javascript/neo_components/controllers/notification_bar_controller.js +9 -0
  146. data/app/javascript/neo_components/controllers/pagination_controller.js +11 -0
  147. data/app/javascript/neo_components/controllers/tab_change_controller.js +23 -0
  148. data/app/javascript/neo_components/controllers/tabs_controller.js +29 -0
  149. data/app/javascript/neo_components/controllers/text_clamp_controller.js +29 -0
  150. data/app/views/shared/components/_progress_bar_short.html.erb +8 -0
  151. data/app/views/shared/components/_tooltip.html.erb +7 -0
  152. data/app/views/view_components/accordion_component/_accordion.html.erb +22 -0
  153. data/app/views/view_components/breadcrumbs_component/_breadcrumbs.html.erb +38 -0
  154. data/app/views/view_components/button_component/_button.html.erb +13 -0
  155. data/app/views/view_components/buttons/_danger.html.erb +30 -0
  156. data/app/views/view_components/buttons/_primary.html.erb +31 -0
  157. data/app/views/view_components/buttons/_secondary.html.erb +29 -0
  158. data/app/views/view_components/chip_component/_chip_component.html.erb +9 -0
  159. data/app/views/view_components/course_carousal/_course_card_component.html.erb +65 -0
  160. data/app/views/view_components/course_carousal/_course_carousal_body_component.html.erb +25 -0
  161. data/app/views/view_components/course_carousal/_course_carousal_component.html.erb +8 -0
  162. data/app/views/view_components/course_carousal/_long_course_card_component.html.erb +70 -0
  163. data/app/views/view_components/course_select/_course_select_component.html.erb +19 -0
  164. data/app/views/view_components/course_select/_list_component.html.erb +14 -0
  165. data/app/views/view_components/course_select/_list_item_component.html.erb +86 -0
  166. data/app/views/view_components/course_select/_load_more.html.erb +17 -0
  167. data/app/views/view_components/course_select/_search_component.html.erb +48 -0
  168. data/app/views/view_components/course_select/_sidebar_component.html.erb +41 -0
  169. data/app/views/view_components/doc_section/_doc_section_component.html.erb +6 -0
  170. data/app/views/view_components/inputs/_checkbox_field.html.erb +19 -0
  171. data/app/views/view_components/inputs/_date_select_component.html.erb +37 -0
  172. data/app/views/view_components/inputs/_dropdown_field.html.erb +22 -0
  173. data/app/views/view_components/inputs/_file_selector.html.erb +16 -0
  174. data/app/views/view_components/inputs/_input_checkbox_component.html.erb +7 -0
  175. data/app/views/view_components/inputs/_input_mobile_component.html.erb +16 -0
  176. data/app/views/view_components/inputs/_input_radio_component.html.erb +7 -0
  177. data/app/views/view_components/inputs/_input_text_component.html.erb +16 -0
  178. data/app/views/view_components/inputs/_mobile_field.html.erb +31 -0
  179. data/app/views/view_components/inputs/_radio_field.html.erb +25 -0
  180. data/app/views/view_components/inputs/_text_field.html.erb +52 -0
  181. data/app/views/view_components/inputs/_textarea.html.erb +26 -0
  182. data/app/views/view_components/inputs/date_picker_component/_date_picker.html.erb +16 -0
  183. data/app/views/view_components/inputs/date_picker_component/_input_box.html.erb +34 -0
  184. data/app/views/view_components/inputs/dropdown_component/_dropdown.html.erb +16 -0
  185. data/app/views/view_components/inputs/dropdown_component/_select_box.html.erb +10 -0
  186. data/app/views/view_components/inputs/file_selector_component/_file_selector_box.html.erb +76 -0
  187. data/app/views/view_components/inputs/input_checkbox/_checkbox.html.erb +20 -0
  188. data/app/views/view_components/inputs/input_mobile/_code.html.erb +13 -0
  189. data/app/views/view_components/inputs/input_mobile/_mobile_box.html.erb +4 -0
  190. data/app/views/view_components/inputs/input_mobile/_number.html.erb +13 -0
  191. data/app/views/view_components/inputs/input_radio/_radio_button.html.erb +14 -0
  192. data/app/views/view_components/inputs/input_text/_text_box.html.erb +33 -0
  193. data/app/views/view_components/inputs/textarea_component/_text_area_box.html.erb +16 -0
  194. data/app/views/view_components/inputs/textarea_component/_textarea.html.erb +16 -0
  195. data/app/views/view_components/member_list/_member_list.html.erb +4 -0
  196. data/app/views/view_components/member_list/_member_search.html.erb +15 -0
  197. data/app/views/view_components/member_list/_members.html.erb +65 -0
  198. data/app/views/view_components/menu_component/_menu_component.html.erb +16 -0
  199. data/app/views/view_components/menu_component/_menu_item_button.html.erb +17 -0
  200. data/app/views/view_components/menu_component/_menu_item_link.html.erb +5 -0
  201. data/app/views/view_components/menu_component_old/_menu_component.html.erb +17 -0
  202. data/app/views/view_components/menu_component_old/_menu_item_button.html.erb +7 -0
  203. data/app/views/view_components/menu_component_old/_menu_item_link.html.erb +5 -0
  204. data/app/views/view_components/modal_component/_modal_box_component.html.erb +28 -0
  205. data/app/views/view_components/modals/_modal_component.html.erb +25 -0
  206. data/app/views/view_components/notification_bar/_notification_bar.html.erb +17 -0
  207. data/app/views/view_components/paginator/_next_page.html.erb +7 -0
  208. data/app/views/view_components/paginator/_paginator_component.html.erb +6 -0
  209. data/app/views/view_components/paginator/_prev_page.html.erb +7 -0
  210. data/app/views/view_components/profile_icon_component/_profile_icon.html.erb +3 -0
  211. data/app/views/view_components/progress_component/_progressbar.html.erb +5 -0
  212. data/app/views/view_components/typography/_h1_component.html.erb +1 -0
  213. data/app/views/view_components/typography/_h2_component.html.erb +1 -0
  214. data/app/views/view_components/typography/_h3_component.html.erb +1 -0
  215. data/app/views/view_components/typography/_heading_component.html.erb +3 -0
  216. data/app/views/view_components/typography/_link_component.html.erb +1 -0
  217. data/app/views/view_components/typography/_linked_text_component.html.erb +3 -0
  218. data/app/views/view_components/typography/_text_component.html.erb +1 -0
  219. data/config/importmap.rb +5 -0
  220. data/lib/neo_components/engine.rb +38 -0
  221. data/lib/neo_components/version.rb +5 -0
  222. data/lib/neo_components.rb +4 -0
  223. metadata +327 -0
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module ButtonComponent
5
+ class ButtonComponent
6
+ BUTTON_TYPES = %w[solid outline].freeze
7
+ BUTTON_SIZES = %w[sm md lg].freeze
8
+ COLOR_SCHEMES = %w[primary secondary danger].freeze
9
+ ICON_POSITIONS = %w[left right].freeze
10
+ TOOLTIP_POSITIONS = %w[top left bottom right].freeze
11
+ DISABLED_CLASS = %w[opacity-50 cursor-not-allowed].freeze
12
+
13
+ BUTTON_SIZE_MAPPING = {
14
+ sm: 'btn-sm',
15
+ md: 'btn-md',
16
+ lg: 'btn-lg'
17
+ }.freeze
18
+
19
+ BUTTON_COLOUR_SCHEME_MAPPING = {
20
+ solid: {
21
+ primary: 'btn-solid-primary',
22
+ secondary: 'btn-solid-secondary',
23
+ danger: 'btn-solid-danger'
24
+ },
25
+ outline: {
26
+ primary: 'btn-outline-primary',
27
+ secondary: 'btn-outline-secondary',
28
+ danger: 'btn-outline-danger'
29
+ }
30
+ }.freeze
31
+
32
+ BUTTON_DISABLED_MAPPING = {
33
+ solid: {
34
+ primary: 'btn-solid-primary-disabled',
35
+ secondary: 'btn-solid-secondary-disabled',
36
+ danger: 'btn-solid-danger-disabled'
37
+ },
38
+ outline: {
39
+ primary: 'btn-outline-primary-disabled',
40
+ secondary: 'btn-outline-secondary-disabled',
41
+ danger: 'btn-outline-danger-disabled'
42
+ }
43
+ }.freeze
44
+
45
+ ICON_STYLES_MAPPING = {
46
+ sm: 'btn-icon-sm',
47
+ md: 'btn-icon-md',
48
+ lg: 'btn-icon-lg'
49
+ }.freeze
50
+
51
+ BUTTON_TEXT_STYLE_MAPPING = {
52
+ sm: 'btn-text-sm',
53
+ md: 'btn-text-md',
54
+ lg: 'btn-text-lg'
55
+ }.freeze
56
+
57
+ attr_accessor :text, :type, :colorscheme, :size, :icon_name, :icon_position, :tooltip_text, :tooltip_position,
58
+ :disabled, :hide_label_in_mobile, :html_options, :color_mapping
59
+
60
+ def initialize(text:, type:, colorscheme:, size:, icon_name:, icon_position:, tooltip_text:, tooltip_position:,
61
+ disabled:, hide_label_in_mobile:, html_options:)
62
+ self.text = text
63
+ self.type = type
64
+ self.size = size
65
+ self.colorscheme = colorscheme
66
+ self.icon_name = icon_name
67
+ self.icon_position = icon_position
68
+ self.tooltip_text = tooltip_text
69
+ self.tooltip_position = tooltip_position
70
+ self.disabled = disabled
71
+ self.hide_label_in_mobile = hide_label_in_mobile
72
+ self.html_options = html_options
73
+ # validation
74
+ raise "Incorrect button type: #{type}" unless BUTTON_TYPES.include? type
75
+ raise "Incorrect button size: #{size}" unless BUTTON_SIZES.include? size
76
+ raise "Incorrect color scheme: #{colorscheme}" unless COLOR_SCHEMES.include? colorscheme
77
+ raise "Incorrect icon_position: #{icon_position}" unless ICON_POSITIONS.include? icon_position
78
+ raise "Incorrect tooltip_position: #{tooltip_position}" unless TOOLTIP_POSITIONS.include? tooltip_position
79
+
80
+ self.color_mapping = color_scheme_mapping
81
+ end
82
+
83
+ def base_styles
84
+ styles = ['btn-base']
85
+ styles.append('btn-disabled') if disabled
86
+ styles.join(' ')
87
+ end
88
+
89
+ def color_styles
90
+ color_mapping
91
+ end
92
+
93
+ def sizing_styles
94
+ BUTTON_SIZE_MAPPING[size.to_sym]
95
+ end
96
+
97
+ def text_styles
98
+ size_styles = BUTTON_TEXT_STYLE_MAPPING[size.to_sym]
99
+ styles = ['btn-text-base']
100
+ styles.append('hidden md:flex') if hide_label_in_mobile
101
+ styles.append(size_styles)
102
+ styles.join(' ')
103
+ end
104
+
105
+ def icon_styles
106
+ ICON_STYLES_MAPPING[size.to_sym]
107
+ end
108
+
109
+ private
110
+
111
+ def color_scheme_mapping
112
+ if disabled
113
+ ['btn-disabled', BUTTON_DISABLED_MAPPING[type.to_sym][colorscheme.to_sym]]
114
+ else
115
+ BUTTON_COLOUR_SCHEME_MAPPING[type.to_sym][colorscheme.to_sym]
116
+ end
117
+ end
118
+ end
119
+
120
+ def button(
121
+ label: nil,
122
+ type: 'primary',
123
+ size: 'md',
124
+ icon_name: nil,
125
+ icon_position: 'left',
126
+ tooltip_text: '',
127
+ tooltip_position: 'bottom',
128
+ disabled: false,
129
+ html_options: {}
130
+ )
131
+ render(
132
+ partial: "view_components/buttons/#{type}",
133
+ locals: {
134
+ label:,
135
+ type:,
136
+ size:,
137
+ icon_name:,
138
+ icon_position:,
139
+ tooltip_text:,
140
+ tooltip_position:,
141
+ disabled:,
142
+ html_options:
143
+ }
144
+ )
145
+ end
146
+
147
+ def button_component(
148
+ text: '',
149
+ type: 'solid',
150
+ size: 'md',
151
+ colorscheme: 'primary',
152
+ icon_name: '',
153
+ icon_position: 'left',
154
+ tooltip_text: '',
155
+ tooltip_position: 'bottom',
156
+ disabled: false,
157
+ hide_label_in_mobile: false,
158
+ html_options: {}
159
+ )
160
+ button = ButtonComponent.new(
161
+ text:,
162
+ type:,
163
+ size:,
164
+ colorscheme:,
165
+ icon_name:,
166
+ icon_position:,
167
+ tooltip_text:,
168
+ tooltip_position:,
169
+ disabled:,
170
+ hide_label_in_mobile:,
171
+ html_options:
172
+ )
173
+
174
+ render(
175
+ partial: 'view_components/button_component/button',
176
+ locals: { button: }
177
+ )
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module ChipComponent
5
+ COLOR_SCHEMES = {
6
+ 'primary' => %w[
7
+ bg-primary-light-100
8
+ text-primary-dark
9
+ text-primary
10
+ text-primary
11
+ border-0
12
+ ],
13
+ 'primary_lite' => %w[
14
+ bg-primary-light-50
15
+ text-primary-dark
16
+ text-primary
17
+ text-primary
18
+ border-0
19
+ ],
20
+ 'danger' => %w[
21
+ bg-danger-light
22
+ text-primary-dark
23
+ text-danger-dark
24
+ text-danger-dark
25
+ border-0
26
+ ],
27
+ 'input' => %w[
28
+ bg-primary-light-50
29
+ text-primary-dark
30
+ text-primary
31
+ text-primary
32
+ border-slate-grey-light
33
+ ]
34
+ }.freeze
35
+
36
+ def chip_component(text: '', icon_name: nil, close: false, colorscheme: 'primary')
37
+ styles = COLOR_SCHEMES[colorscheme]
38
+ raise "Incorrect color scheme #{colorscheme}" unless styles
39
+
40
+ bg_style, text_style, icon_style, close_style, border_style = styles
41
+
42
+ render partial: 'view_components/chip_component/chip_component', locals: {
43
+ text:,
44
+ icon_name:,
45
+ close:,
46
+ bg_style:,
47
+ text_style:,
48
+ icon_style:,
49
+ close_style:,
50
+ border_style:
51
+ }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ # To be used only within a component class such as ButtonComponent
5
+ module ComponentHelper
6
+ def class_list(base, *styles)
7
+ base.concat(styles.compact)
8
+ base.join(' ')
9
+ end
10
+
11
+ def resolve_error(form, name, explicit_error)
12
+ return explicit_error if explicit_error.present?
13
+ return if form.blank? || form.object.blank? || name.blank?
14
+
15
+ attribute = name.to_s.split('[').last.delete(']').humanize
16
+ message = form.object.errors[name].first
17
+
18
+ message.present? ? "#{attribute} #{message}" : nil
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module CourseCardComponent
5
+ def course_card_component(course:, enrollment: nil)
6
+ render partial: 'view_components/course_carousal/course_card_component', locals: { course:, enrollment: }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module CourseCarousalComponent
5
+ def course_carousal_component(courses:, title:, count:, load_path:)
6
+ render partial: 'view_components/course_carousal/course_carousal_component',
7
+ locals: { courses:, title:, count:, load_path: }
8
+ end
9
+
10
+ def course_carousal_body_component(courses:)
11
+ render partial: 'view_components/course_carousal/course_carousal_body_component', locals: { courses: }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module CourseSelectComponent
5
+ def course_select_component(search_context:, submit_path:, courses: [], tags: [], cancel_link: nil,
6
+ show_duration: false)
7
+ render partial: 'view_components/course_select/course_select_component',
8
+ locals: { search_context:, courses:, tags:, cancel_link:, show_duration:, submit_path: }
9
+ end
10
+
11
+ def _course_select_search_component(search_context:, tags:)
12
+ render partial: 'view_components/course_select/search_component', locals: { search_context:, tags: }
13
+ end
14
+
15
+ def _course_select_sidebar_component(form:, tags:)
16
+ render partial: 'view_components/course_select/sidebar_component', locals: { form:, tags: }
17
+ end
18
+
19
+ def _course_select_list_component(search_context:, courses:)
20
+ render partial: 'view_components/course_select/list_component',
21
+ locals: { search_context:, courses: }
22
+ end
23
+
24
+ def _course_select_list_item_component(course:)
25
+ render partial: 'view_components/course_select/list_item_component', locals: { course: }
26
+ end
27
+
28
+ def _course_select_load_more(search_context:, courses:)
29
+ render partial: 'view_components/course_select/load_more', locals: { search_context:, courses: }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module DocSectionComponent
5
+ def doc_section_component(title: '', &)
6
+ block_content = capture(&) if block_given?
7
+ render partial: 'view_components/doc_section/doc_section_component', locals: { title:, block: block_content }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module IconComponent
5
+ ICONS_PATH = NeoComponents::Engine.gem_root.join("app", "assets", "icons")
6
+
7
+ def icon(icon_name, css: nil, span_css: nil, stroke_width: nil)
8
+ file = Rails.cache.fetch("neo_components:icon:#{icon_name}") do
9
+ ICONS_PATH.join("#{icon_name}.svg").read
10
+ end
11
+
12
+ svg = Nokogiri::HTML::DocumentFragment.parse(file).at_css('svg')
13
+ svg['class'] = css
14
+ if stroke_width.present?
15
+ # Use the stroke_width passed from helper
16
+ svg['stroke-width'] = stroke_width
17
+ elsif svg['stroke-width'].blank?
18
+ # Use default only if SVG does not have stroke-width
19
+ svg['stroke-width'] = '1.5'
20
+ end
21
+ content_tag(:span, svg.to_html.html_safe, class: "flex justify-center items-center #{span_css}".strip) # rubocop:disable Rails/OutputSafety
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module InputComponent
5
+ class DatePickerComponent
6
+ include ViewComponent::ComponentHelper
7
+
8
+ DATE_PICKER_SIZES = %w[md lg].freeze
9
+
10
+ DATE_PICKER_SIZE_STYLE = {
11
+ md: 'input-text-div-base-md main-text-md-normal',
12
+ lg: 'input-text-div-base-lg main-text-lg-medium'
13
+ }.freeze
14
+
15
+ LABEL_STYLES = {
16
+ md: 'input-text-label-md general-text-sm-normal',
17
+ lg: 'input-text-label-lg general-text-md-normal'
18
+ }.freeze
19
+
20
+ SUPPORT_TEXT_STYLES = {
21
+ md: 'input-text-subtext-md general-text-sm-normal',
22
+ lg: 'input-text-subtext-lg general-text-md-normal'
23
+ }.freeze
24
+
25
+ ICON_SIZE = {
26
+ md: 'input-text-icon-md',
27
+ lg: 'input-text-icon-lg'
28
+ }.freeze
29
+
30
+ attr_accessor :form, :name, :label, :value, :placeholder, :size,
31
+ :support_text, :error, :disabled, :html_options
32
+
33
+ def initialize(form:, name:, label:, value:, placeholder:, size:, support_text:, error:, disabled:, html_options:)
34
+ raise "Incorrect date picker size: #{size}" unless DATE_PICKER_SIZES.include?(size)
35
+
36
+ error_message = resolve_error(form, name, error)
37
+
38
+ self.form = form
39
+ self.name = name
40
+ self.label = label
41
+ self.value = value
42
+ self.placeholder = placeholder
43
+ self.size = size
44
+ self.support_text = (error_message.presence || support_text)
45
+ self.error = error_message
46
+ self.disabled = disabled
47
+ self.html_options = html_options
48
+
49
+ self.html_options[:disabled] = true if disabled
50
+ self.html_options[:class] = date_picker_style
51
+ end
52
+
53
+ def date_picker_style
54
+ base = ['w-full border']
55
+ size_style = DATE_PICKER_SIZE_STYLE[size.to_sym]
56
+
57
+ color_style =
58
+ if disabled
59
+ 'border-disabled-color text-disabled-color placeholder-disabled-color'
60
+ elsif error.present?
61
+ 'text-danger focus:text-danger-dark border-danger focus:border-danger focus:ring-danger'
62
+ else
63
+ 'text-letter-color-light border-slate-grey-50 focus:ring-primary'
64
+ end
65
+
66
+ class_list(base, size_style, color_style)
67
+ end
68
+
69
+ def label_style
70
+ size_style = LABEL_STYLES[size.to_sym]
71
+
72
+ color_style =
73
+ if disabled
74
+ 'text-disabled-color'
75
+ elsif error.present?
76
+ 'text-danger-dark'
77
+ else
78
+ 'text-letter-color-light group-focus-within:text-primary'
79
+ end
80
+
81
+ class_list([], size_style, color_style)
82
+ end
83
+
84
+ def support_text_style
85
+ size_style = SUPPORT_TEXT_STYLES[size.to_sym]
86
+
87
+ color_style =
88
+ if disabled
89
+ 'text-disabled-color'
90
+ elsif error.present?
91
+ 'text-danger-dark'
92
+ else
93
+ 'text-letter-color-light'
94
+ end
95
+
96
+ class_list([], size_style, color_style)
97
+ end
98
+
99
+ def icon_wrapper_style
100
+ base = ['date-picker-icon-wrapper']
101
+
102
+ disabled_style =
103
+ if disabled
104
+ 'date-picker-icon-wrapper-disabled'
105
+ else
106
+ ''
107
+ end
108
+
109
+ class_list(base, disabled_style)
110
+ end
111
+
112
+ def icon_style
113
+ size_style = ICON_SIZE[size.to_sym]
114
+
115
+ color_style =
116
+ if disabled
117
+ 'text-disabled-color'
118
+ elsif error.present?
119
+ 'text-danger'
120
+ else
121
+ 'text-letter-color-light hover:text-primary'
122
+ end
123
+ class_list([], size_style, color_style)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module InputComponent
5
+ class DropdownComponent
6
+ include ViewComponent::ComponentHelper
7
+
8
+ DROPDOWN_SIZES = %w[md lg].freeze
9
+
10
+ DROPDOWN_SIZE_STYLE = {
11
+ md: 'dropdown-component-md main-text-md-normal',
12
+ lg: 'dropdown-component-lg main-text-lg-medium'
13
+ }.freeze
14
+
15
+ LABEL_STYLES = {
16
+ md: 'input-text-label-md general-text-sm-normal',
17
+ lg: 'input-text-label-lg general-text-md-normal'
18
+ }.freeze
19
+
20
+ SUPPORT_TEXT_STYLES = {
21
+ md: 'input-text-subtext-md general-text-sm-normal',
22
+ lg: 'input-text-subtext-lg general-text-md-normal'
23
+ }.freeze
24
+
25
+ attr_accessor :form, :name, :label, :options, :value, :size,
26
+ :support_text, :error, :disabled, :html_options, :prompt
27
+
28
+ def initialize(form:, name:, label:, options:, value:, size:, support_text:, error:, disabled:, html_options:,
29
+ prompt:)
30
+ raise "Incorrect dropdown size: #{size}" unless DROPDOWN_SIZES.include?(size)
31
+
32
+ error_message = resolve_error(form, name, error)
33
+
34
+ self.form = form
35
+ self.name = name
36
+ self.label = label
37
+ self.options = options
38
+ self.value = value
39
+ self.size = size
40
+ self.support_text = (error_message.presence || support_text)
41
+ self.error = error_message
42
+ self.disabled = disabled
43
+ self.html_options = html_options
44
+ self.prompt = prompt
45
+
46
+ self.html_options[:disabled] = true if disabled
47
+ self.html_options[:class] = dropdown_style
48
+ end
49
+
50
+ def dropdown_style
51
+ base = ['dropdown-component-base']
52
+ size_style = DROPDOWN_SIZE_STYLE[size.to_sym]
53
+
54
+ color_style =
55
+ if disabled
56
+ 'text-disabled-color border-disabled-color'
57
+ elsif error.present?
58
+ 'text-danger focus:text-danger-dark border-danger focus:border-danger focus:ring-danger'
59
+ else
60
+ 'text-letter-color-light border-slate-grey-50 focus:ring-primary'
61
+ end
62
+
63
+ class_list(base, size_style, color_style)
64
+ end
65
+
66
+ def label_style
67
+ base = []
68
+ size_style = LABEL_STYLES[size.to_sym]
69
+
70
+ color_style =
71
+ if disabled
72
+ 'text-disabled-color'
73
+ elsif error.present?
74
+ 'text-danger-dark'
75
+ else
76
+ 'text-letter-color-light group-focus-within:text-primary'
77
+ end
78
+
79
+ class_list(base, size_style, color_style)
80
+ end
81
+
82
+ def support_text_style
83
+ base = []
84
+ size_style = SUPPORT_TEXT_STYLES[size.to_sym]
85
+
86
+ color_style =
87
+ if disabled
88
+ 'text-disabled-color'
89
+ elsif error.present?
90
+ 'text-danger-dark'
91
+ else
92
+ 'text-letter-color-light'
93
+ end
94
+
95
+ class_list(base, size_style, color_style)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module InputComponent
5
+ class FileSelectorComponent
6
+ include ViewComponent::ComponentHelper
7
+
8
+ FILE_SELECTOR_TYPES = %w[image document video].freeze
9
+
10
+ attr_accessor :form, :name, :label, :support_text,
11
+ :support_text_two, :error, :disabled, :html_options, :type
12
+
13
+ def initialize(type:, form: nil, name: nil, label: nil,
14
+ support_text: nil, support_text_two: nil, error: nil, disabled: false, html_options: {})
15
+ raise "Invalid or missing file type: #{type}" unless FILE_SELECTOR_TYPES.include?(type)
16
+
17
+ error_message = resolve_error(form, name, error)
18
+
19
+ self.form = form
20
+ self.name = name
21
+ self.label = label
22
+ self.support_text = (error_message.presence || support_text)
23
+ self.support_text_two = support_text_two
24
+ self.error = error_message
25
+ self.disabled = disabled
26
+ self.html_options = html_options
27
+ self.type = type
28
+ end
29
+
30
+ def accepted_types
31
+ case type
32
+ when 'image'
33
+ 'image/*'
34
+ when 'document'
35
+ '.csv,.pdf,.doc,.docx'
36
+ when 'video'
37
+ 'video/*,.mp4,.mov,.avi,.mkv'
38
+ else
39
+ '*/*'
40
+ end
41
+ end
42
+
43
+ def wrapper_style
44
+ base = ['file-selector-component-base radius-lg md:radius-xl']
45
+
46
+ color_style =
47
+ if disabled
48
+ 'textarea-disabled-state pointer-events-none cursor-not-allowed'
49
+ elsif error.present?
50
+ 'border-danger'
51
+ else
52
+ 'border-slate-grey-50 hover:border-primary-light focus:border-primary-light'
53
+ end
54
+
55
+ class_list(base, color_style)
56
+ end
57
+
58
+ def label_style
59
+ base = ['general-text-sm-normal px-1']
60
+
61
+ color_style =
62
+ if disabled
63
+ 'text-disabled-color'
64
+ elsif error.present?
65
+ 'text-danger-dark'
66
+ else
67
+ 'text-letter-color-light'
68
+ end
69
+
70
+ class_list(base, color_style)
71
+ end
72
+
73
+ def preview_style
74
+ base = ['file-selector-preview']
75
+
76
+ class_list(base)
77
+ end
78
+
79
+ def upload_icon_style
80
+ base = ['file-selector-upload-icon']
81
+
82
+ color_style =
83
+ if disabled
84
+ 'fill-disabled-color'
85
+ elsif error.present?
86
+ 'fill-danger'
87
+ else
88
+ 'fill-primary'
89
+ end
90
+
91
+ class_list(base, color_style)
92
+ end
93
+
94
+ def choose_file_style
95
+ base = ['general-text-md-normal ']
96
+
97
+ color_style =
98
+ if disabled
99
+ 'text-disabled-color'
100
+ else
101
+ 'text-primary'
102
+ end
103
+
104
+ class_list(base, color_style)
105
+ end
106
+
107
+ def support_text_style
108
+ base = ['file-selector-support-text general-text-sm-normal']
109
+
110
+ color_style =
111
+ if disabled
112
+ 'text-disabled-color'
113
+ elsif error.present?
114
+ 'text-danger-dark'
115
+ else
116
+ 'text-slate-grey-50'
117
+ end
118
+
119
+ class_list(base, color_style)
120
+ end
121
+ end
122
+ end
123
+ end