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.
- checksums.yaml +7 -0
- data/app/assets/icons/academy.svg +3 -0
- data/app/assets/icons/adjustments-horizontal.svg +3 -0
- data/app/assets/icons/adjustments-vertical.svg +3 -0
- data/app/assets/icons/arrow-down-tray.svg +3 -0
- data/app/assets/icons/arrow-left.svg +3 -0
- data/app/assets/icons/arrow-long-down.svg +3 -0
- data/app/assets/icons/arrow-long-right.svg +3 -0
- data/app/assets/icons/arrow-long-up.svg +3 -0
- data/app/assets/icons/arrow-right.svg +3 -0
- data/app/assets/icons/arrow-up-tray.svg +3 -0
- data/app/assets/icons/arrow-uturn-left.svg +3 -0
- data/app/assets/icons/arrow-uturn-right.svg +3 -0
- data/app/assets/icons/assign-user.svg +13 -0
- data/app/assets/icons/at-symbol.svg +3 -0
- data/app/assets/icons/bars-4.svg +3 -0
- data/app/assets/icons/bell.svg +3 -0
- data/app/assets/icons/book.svg +3 -0
- data/app/assets/icons/bookmark.svg +3 -0
- data/app/assets/icons/building-office-2.svg +3 -0
- data/app/assets/icons/calendar.svg +3 -0
- data/app/assets/icons/celebration.svg +15 -0
- data/app/assets/icons/check-badge-solid.svg +3 -0
- data/app/assets/icons/check-circle-solid.svg +3 -0
- data/app/assets/icons/check-circle.svg +3 -0
- data/app/assets/icons/check-list.svg +12 -0
- data/app/assets/icons/check.svg +3 -0
- data/app/assets/icons/chevron-double-right.svg +3 -0
- data/app/assets/icons/chevron-down.svg +3 -0
- data/app/assets/icons/chevron-left.svg +3 -0
- data/app/assets/icons/chevron-right.svg +3 -0
- data/app/assets/icons/chevron-up.svg +3 -0
- data/app/assets/icons/circle-stack.svg +3 -0
- data/app/assets/icons/clipboard-document-check-solid.svg +4 -0
- data/app/assets/icons/clipboard-document-check.svg +3 -0
- data/app/assets/icons/clock.svg +3 -0
- data/app/assets/icons/complete.svg +3 -0
- data/app/assets/icons/course.svg +3 -0
- data/app/assets/icons/device-phone-mobile.svg +3 -0
- data/app/assets/icons/document-arrow-up.svg +4 -0
- data/app/assets/icons/document-text.svg +4 -0
- data/app/assets/icons/ellipsis-vertical.svg +3 -0
- data/app/assets/icons/exclaimation-circle-solid.svg +3 -0
- data/app/assets/icons/eye.svg +4 -0
- data/app/assets/icons/flag.svg +3 -0
- data/app/assets/icons/funnel.svg +3 -0
- data/app/assets/icons/green-circle.svg +3 -0
- data/app/assets/icons/green-tick.svg +3 -0
- data/app/assets/icons/grid.svg +6 -0
- data/app/assets/icons/home.svg +3 -0
- data/app/assets/icons/i-vector.svg +3 -0
- data/app/assets/icons/lessons.svg +11 -0
- data/app/assets/icons/magnifying-glass.svg +3 -0
- data/app/assets/icons/minus.svg +3 -0
- data/app/assets/icons/numbered-list.svg +3 -0
- data/app/assets/icons/pencil.svg +3 -0
- data/app/assets/icons/play.svg +3 -0
- data/app/assets/icons/plus.svg +3 -0
- data/app/assets/icons/power.svg +3 -0
- data/app/assets/icons/question-mark-circle.svg +3 -0
- data/app/assets/icons/quiz-score.svg +15 -0
- data/app/assets/icons/re-invite.svg +3 -0
- data/app/assets/icons/red-circle.svg +3 -0
- data/app/assets/icons/share.svg +3 -0
- data/app/assets/icons/smiley-five.svg +5 -0
- data/app/assets/icons/smiley-four.svg +5 -0
- data/app/assets/icons/smiley-one.svg +5 -0
- data/app/assets/icons/smiley-three.svg +5 -0
- data/app/assets/icons/smiley-two.svg +5 -0
- data/app/assets/icons/sparkle.svg +3 -0
- data/app/assets/icons/star-transparent.svg +3 -0
- data/app/assets/icons/stop.svg +4 -0
- data/app/assets/icons/support-placeholder.svg +4 -0
- data/app/assets/icons/tag.svg +4 -0
- data/app/assets/icons/timer.svg +5 -0
- data/app/assets/icons/transcript.svg +31 -0
- data/app/assets/icons/trash.svg +3 -0
- data/app/assets/icons/upload-info.svg +7 -0
- data/app/assets/icons/user-circle.svg +3 -0
- data/app/assets/icons/user-group.svg +3 -0
- data/app/assets/icons/user.svg +3 -0
- data/app/assets/icons/users.svg +3 -0
- data/app/assets/icons/winner.svg +35 -0
- data/app/assets/icons/x-circle-solid.svg +3 -0
- data/app/assets/icons/x-circle.svg +3 -0
- data/app/assets/icons/x-mark.svg +3 -0
- data/app/assets/stylesheets/breadcrumbs.tailwind.css +45 -0
- data/app/assets/stylesheets/buttons.tailwind.css +98 -0
- data/app/assets/stylesheets/course_progress.css +75 -0
- data/app/assets/stylesheets/custom.css +67 -0
- data/app/assets/stylesheets/date_picker.tailwind.css +7 -0
- data/app/assets/stylesheets/directives.tailwind.css +3 -0
- data/app/assets/stylesheets/dropdown.tailwind.css +11 -0
- data/app/assets/stylesheets/file_selector.tailwind.css +47 -0
- data/app/assets/stylesheets/icons.css.erb +244 -0
- data/app/assets/stylesheets/inputs.tailwind.css +53 -0
- data/app/assets/stylesheets/menu_component.tailwind.css +39 -0
- data/app/assets/stylesheets/mobile_inputs.tailwind.css +11 -0
- data/app/assets/stylesheets/modalbox.tailwind.css +35 -0
- data/app/assets/stylesheets/profile_icon.tailwind.css +15 -0
- data/app/assets/stylesheets/textarea.tailwind.css +43 -0
- data/app/assets/stylesheets/tooltip.css +88 -0
- data/app/assets/stylesheets/typography.tailwind.css +70 -0
- data/app/helpers/ui_helper.rb +31 -0
- data/app/helpers/view_component/accordion_component.rb +12 -0
- data/app/helpers/view_component/breadcrumbs_component.rb +30 -0
- data/app/helpers/view_component/button_component.rb +180 -0
- data/app/helpers/view_component/chip_component.rb +54 -0
- data/app/helpers/view_component/component_helper.rb +21 -0
- data/app/helpers/view_component/course_card_component.rb +9 -0
- data/app/helpers/view_component/course_carousal_component.rb +14 -0
- data/app/helpers/view_component/course_select_component.rb +32 -0
- data/app/helpers/view_component/doc_section_component.rb +10 -0
- data/app/helpers/view_component/icon_component.rb +24 -0
- data/app/helpers/view_component/input_component/date_picker_component.rb +127 -0
- data/app/helpers/view_component/input_component/dropdown_component.rb +99 -0
- data/app/helpers/view_component/input_component/file_selector_component.rb +123 -0
- data/app/helpers/view_component/input_component/input_checkbox_component.rb +54 -0
- data/app/helpers/view_component/input_component/input_mobile_component.rb +68 -0
- data/app/helpers/view_component/input_component/input_radio_component.rb +54 -0
- data/app/helpers/view_component/input_component/input_text_component.rb +137 -0
- data/app/helpers/view_component/input_component/textarea_component.rb +101 -0
- data/app/helpers/view_component/input_component.rb +290 -0
- data/app/helpers/view_component/input_textarea_component.rb +28 -0
- data/app/helpers/view_component/long_course_card_component.rb +10 -0
- data/app/helpers/view_component/member_list_component.rb +17 -0
- data/app/helpers/view_component/menu_component.rb +31 -0
- data/app/helpers/view_component/menu_component_helper.rb +22 -0
- data/app/helpers/view_component/menu_item.rb +12 -0
- data/app/helpers/view_component/modal_box_component.rb +29 -0
- data/app/helpers/view_component/modal_component.rb +12 -0
- data/app/helpers/view_component/notification_bar_component.rb +22 -0
- data/app/helpers/view_component/paginator_component.rb +9 -0
- data/app/helpers/view_component/profile_icon_component.rb +46 -0
- data/app/helpers/view_component/progress_component.rb +12 -0
- data/app/helpers/view_component/table_component.rb +22 -0
- data/app/helpers/view_component/typography_component.rb +83 -0
- data/app/javascript/neo_components/controllers/collapsible_controller.js +37 -0
- data/app/javascript/neo_components/controllers/date_picker_controller.js +17 -0
- data/app/javascript/neo_components/controllers/file_selector_controller.js +145 -0
- data/app/javascript/neo_components/controllers/input_mobile_controller.js +7 -0
- data/app/javascript/neo_components/controllers/menu_component_controller.js +26 -0
- data/app/javascript/neo_components/controllers/modal_loader_controller.js +13 -0
- data/app/javascript/neo_components/controllers/modals_controller.js +26 -0
- data/app/javascript/neo_components/controllers/notification_bar_controller.js +9 -0
- data/app/javascript/neo_components/controllers/pagination_controller.js +11 -0
- data/app/javascript/neo_components/controllers/tab_change_controller.js +23 -0
- data/app/javascript/neo_components/controllers/tabs_controller.js +29 -0
- data/app/javascript/neo_components/controllers/text_clamp_controller.js +29 -0
- data/app/views/shared/components/_progress_bar_short.html.erb +8 -0
- data/app/views/shared/components/_tooltip.html.erb +7 -0
- data/app/views/view_components/accordion_component/_accordion.html.erb +22 -0
- data/app/views/view_components/breadcrumbs_component/_breadcrumbs.html.erb +38 -0
- data/app/views/view_components/button_component/_button.html.erb +13 -0
- data/app/views/view_components/buttons/_danger.html.erb +30 -0
- data/app/views/view_components/buttons/_primary.html.erb +31 -0
- data/app/views/view_components/buttons/_secondary.html.erb +29 -0
- data/app/views/view_components/chip_component/_chip_component.html.erb +9 -0
- data/app/views/view_components/course_carousal/_course_card_component.html.erb +65 -0
- data/app/views/view_components/course_carousal/_course_carousal_body_component.html.erb +25 -0
- data/app/views/view_components/course_carousal/_course_carousal_component.html.erb +8 -0
- data/app/views/view_components/course_carousal/_long_course_card_component.html.erb +70 -0
- data/app/views/view_components/course_select/_course_select_component.html.erb +19 -0
- data/app/views/view_components/course_select/_list_component.html.erb +14 -0
- data/app/views/view_components/course_select/_list_item_component.html.erb +86 -0
- data/app/views/view_components/course_select/_load_more.html.erb +17 -0
- data/app/views/view_components/course_select/_search_component.html.erb +48 -0
- data/app/views/view_components/course_select/_sidebar_component.html.erb +41 -0
- data/app/views/view_components/doc_section/_doc_section_component.html.erb +6 -0
- data/app/views/view_components/inputs/_checkbox_field.html.erb +19 -0
- data/app/views/view_components/inputs/_date_select_component.html.erb +37 -0
- data/app/views/view_components/inputs/_dropdown_field.html.erb +22 -0
- data/app/views/view_components/inputs/_file_selector.html.erb +16 -0
- data/app/views/view_components/inputs/_input_checkbox_component.html.erb +7 -0
- data/app/views/view_components/inputs/_input_mobile_component.html.erb +16 -0
- data/app/views/view_components/inputs/_input_radio_component.html.erb +7 -0
- data/app/views/view_components/inputs/_input_text_component.html.erb +16 -0
- data/app/views/view_components/inputs/_mobile_field.html.erb +31 -0
- data/app/views/view_components/inputs/_radio_field.html.erb +25 -0
- data/app/views/view_components/inputs/_text_field.html.erb +52 -0
- data/app/views/view_components/inputs/_textarea.html.erb +26 -0
- data/app/views/view_components/inputs/date_picker_component/_date_picker.html.erb +16 -0
- data/app/views/view_components/inputs/date_picker_component/_input_box.html.erb +34 -0
- data/app/views/view_components/inputs/dropdown_component/_dropdown.html.erb +16 -0
- data/app/views/view_components/inputs/dropdown_component/_select_box.html.erb +10 -0
- data/app/views/view_components/inputs/file_selector_component/_file_selector_box.html.erb +76 -0
- data/app/views/view_components/inputs/input_checkbox/_checkbox.html.erb +20 -0
- data/app/views/view_components/inputs/input_mobile/_code.html.erb +13 -0
- data/app/views/view_components/inputs/input_mobile/_mobile_box.html.erb +4 -0
- data/app/views/view_components/inputs/input_mobile/_number.html.erb +13 -0
- data/app/views/view_components/inputs/input_radio/_radio_button.html.erb +14 -0
- data/app/views/view_components/inputs/input_text/_text_box.html.erb +33 -0
- data/app/views/view_components/inputs/textarea_component/_text_area_box.html.erb +16 -0
- data/app/views/view_components/inputs/textarea_component/_textarea.html.erb +16 -0
- data/app/views/view_components/member_list/_member_list.html.erb +4 -0
- data/app/views/view_components/member_list/_member_search.html.erb +15 -0
- data/app/views/view_components/member_list/_members.html.erb +65 -0
- data/app/views/view_components/menu_component/_menu_component.html.erb +16 -0
- data/app/views/view_components/menu_component/_menu_item_button.html.erb +17 -0
- data/app/views/view_components/menu_component/_menu_item_link.html.erb +5 -0
- data/app/views/view_components/menu_component_old/_menu_component.html.erb +17 -0
- data/app/views/view_components/menu_component_old/_menu_item_button.html.erb +7 -0
- data/app/views/view_components/menu_component_old/_menu_item_link.html.erb +5 -0
- data/app/views/view_components/modal_component/_modal_box_component.html.erb +28 -0
- data/app/views/view_components/modals/_modal_component.html.erb +25 -0
- data/app/views/view_components/notification_bar/_notification_bar.html.erb +17 -0
- data/app/views/view_components/paginator/_next_page.html.erb +7 -0
- data/app/views/view_components/paginator/_paginator_component.html.erb +6 -0
- data/app/views/view_components/paginator/_prev_page.html.erb +7 -0
- data/app/views/view_components/profile_icon_component/_profile_icon.html.erb +3 -0
- data/app/views/view_components/progress_component/_progressbar.html.erb +5 -0
- data/app/views/view_components/typography/_h1_component.html.erb +1 -0
- data/app/views/view_components/typography/_h2_component.html.erb +1 -0
- data/app/views/view_components/typography/_h3_component.html.erb +1 -0
- data/app/views/view_components/typography/_heading_component.html.erb +3 -0
- data/app/views/view_components/typography/_link_component.html.erb +1 -0
- data/app/views/view_components/typography/_linked_text_component.html.erb +3 -0
- data/app/views/view_components/typography/_text_component.html.erb +1 -0
- data/config/importmap.rb +5 -0
- data/lib/neo_components/engine.rb +38 -0
- data/lib/neo_components/version.rb +5 -0
- data/lib/neo_components.rb +4 -0
- metadata +327 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module InputTextareaComponent
|
|
5
|
+
def input_textarea_component(
|
|
6
|
+
form:,
|
|
7
|
+
field_name:,
|
|
8
|
+
label:,
|
|
9
|
+
placeholder:,
|
|
10
|
+
value: nil,
|
|
11
|
+
rows: 5,
|
|
12
|
+
html_options: {}
|
|
13
|
+
)
|
|
14
|
+
html_options = html_options.merge(
|
|
15
|
+
placeholder: placeholder,
|
|
16
|
+
rows: rows
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
render partial: 'view_components/inputs/textarea', locals: {
|
|
20
|
+
form:,
|
|
21
|
+
field_name:,
|
|
22
|
+
label:,
|
|
23
|
+
value:,
|
|
24
|
+
html_options:
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module LongCourseCardComponent
|
|
5
|
+
def long_course_card_component(course:, enrollment: nil, program: nil)
|
|
6
|
+
render partial: 'view_components/course_carousal/long_course_card_component',
|
|
7
|
+
locals: { course:, enrollment:, program: }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module MemberListComponent
|
|
5
|
+
def member_list_component(team:, members:, all_members: false, term: '')
|
|
6
|
+
render partial: 'view_components/member_list/member_list', locals: { team:, members:, all_members:, term: }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def _member_list_member_search_component(team:, all_members:)
|
|
10
|
+
render partial: 'view_components/member_list/member_search', locals: { team:, all_members: }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def member_list_members_component(team:, members:, all_members: false, term: '')
|
|
14
|
+
render partial: 'view_components/member_list/members', locals: { team:, members:, all_members:, term: }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module MenuComponent
|
|
5
|
+
MenuItem = ViewComponent::MenuItem
|
|
6
|
+
|
|
7
|
+
MENU_POSITIONS = {
|
|
8
|
+
left: 'menu-component-left',
|
|
9
|
+
right: 'menu-component-right',
|
|
10
|
+
center: 'menu-component-center'
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def menu_component(menu_items:, position: 'right', html_options: {})
|
|
14
|
+
position_key = position.to_s.downcase.to_sym
|
|
15
|
+
position_class = MENU_POSITIONS[position_key] || MENU_POSITIONS[:right]
|
|
16
|
+
|
|
17
|
+
menu_items_with_options = menu_items.map do |item|
|
|
18
|
+
item.options ||= {}
|
|
19
|
+
item.url = item.url.presence || 'javascript:void(0)'
|
|
20
|
+
item
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
render(
|
|
24
|
+
'view_components/menu_component/menu_component',
|
|
25
|
+
menu_items: menu_items_with_options,
|
|
26
|
+
position_class: position_class,
|
|
27
|
+
html_options:
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module MenuComponentHelper
|
|
5
|
+
MenuItem = ViewComponent::MenuItem
|
|
6
|
+
|
|
7
|
+
def menu_component_old(menu_items:, icon_class: '', html_options: {})
|
|
8
|
+
menu_items_with_options = menu_items.map do |item|
|
|
9
|
+
item.options ||= {}
|
|
10
|
+
item.url = item.url.presence || '#'
|
|
11
|
+
item
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
render(
|
|
15
|
+
'view_components/menu_component_old/menu_component',
|
|
16
|
+
menu_items: menu_items_with_options,
|
|
17
|
+
icon_class:,
|
|
18
|
+
html_options:
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module ModalBoxComponent
|
|
5
|
+
class ModalBoxComponent
|
|
6
|
+
attr_accessor :title, :variant
|
|
7
|
+
|
|
8
|
+
def initialize(title: nil, variant: nil)
|
|
9
|
+
self.title = title
|
|
10
|
+
self.variant = variant
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def modal_box_component(title: nil, variant: nil, modal_footer: nil, html_options: {}, &)
|
|
15
|
+
modal = ModalBoxComponent.new(title:, variant:)
|
|
16
|
+
block_content = capture(&) if block_given?
|
|
17
|
+
|
|
18
|
+
container_class =
|
|
19
|
+
if variant == :success
|
|
20
|
+
'modal-success'
|
|
21
|
+
else
|
|
22
|
+
'modal-container'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
render partial: 'view_components/modal_component/modal_box_component',
|
|
26
|
+
locals: { modal:, body: block_content, modal_footer:, container_class:, html_options: }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module ModalComponent
|
|
5
|
+
# modal box component
|
|
6
|
+
# @param title - modal title
|
|
7
|
+
def modal_component(title:, &)
|
|
8
|
+
block_content = capture(&) if block_given?
|
|
9
|
+
render partial: 'view_components/modals/modal_component', locals: { title:, body: block_content }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module NotificationBarComponent
|
|
5
|
+
def notification_bar(
|
|
6
|
+
text: nil,
|
|
7
|
+
text_color: 'text-letter-color',
|
|
8
|
+
bg_color: 'bg-white',
|
|
9
|
+
icon_color: 'bg-letter-color'
|
|
10
|
+
)
|
|
11
|
+
render(
|
|
12
|
+
partial: 'view_components/notification_bar/notification_bar',
|
|
13
|
+
locals: {
|
|
14
|
+
text:,
|
|
15
|
+
text_color:,
|
|
16
|
+
icon_color:,
|
|
17
|
+
bg_color:
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module ProfileIconComponent
|
|
5
|
+
class ProfileIconComponent
|
|
6
|
+
include ViewComponent::ComponentHelper
|
|
7
|
+
|
|
8
|
+
PROFILE_ICON_SIZES = %w[sm md lg].freeze
|
|
9
|
+
|
|
10
|
+
PROFILE_ICON_SIZE_STYLES = {
|
|
11
|
+
sm: 'profile-icon-sm',
|
|
12
|
+
md: 'profile-icon-md',
|
|
13
|
+
lg: 'profile-icon-lg'
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
attr_accessor :letter, :size
|
|
17
|
+
|
|
18
|
+
def initialize(letter:, size: 'md')
|
|
19
|
+
raise "Incorrect profile icon size: #{size}" unless PROFILE_ICON_SIZES.include?(size)
|
|
20
|
+
|
|
21
|
+
self.letter = (letter.presence || 'U').to_s[0].upcase
|
|
22
|
+
self.size = size
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def profile_icon_style
|
|
26
|
+
base = ['profile-icon-base']
|
|
27
|
+
size_style = PROFILE_ICON_SIZE_STYLES[size.to_sym]
|
|
28
|
+
|
|
29
|
+
class_list(base, size_style)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def profile_icon_component(letter:, size: 'md')
|
|
34
|
+
normalized_letter = (letter.presence || 'U').to_s[0]
|
|
35
|
+
|
|
36
|
+
profile_icon =
|
|
37
|
+
ProfileIconComponent.new(
|
|
38
|
+
letter: normalized_letter,
|
|
39
|
+
size:
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
render partial: 'view_components/profile_icon_component/profile_icon',
|
|
43
|
+
locals: { profile_icon: }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module ProgressComponent
|
|
5
|
+
def progressbar_component(numerator:, denominator:)
|
|
6
|
+
numerator = 0 if numerator.blank?
|
|
7
|
+
denominator = 0 if denominator.blank?
|
|
8
|
+
fill = denominator.zero? ? 0 : (numerator / denominator.to_f * 100).to_i
|
|
9
|
+
render partial: 'view_components/progress_component/progressbar', locals: { numerator:, denominator:, fill: }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module TableComponent
|
|
5
|
+
def table_cell_name(row_data, avatar: false)
|
|
6
|
+
{ partial: 'shared/components/table_cell_name', locals: { row_data:, avatar: } }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def table_cell_role(row_data)
|
|
10
|
+
{ partial: 'shared/components/table_cell_role', locals: { row_data: } }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def table_actions(row_data, actions = [])
|
|
14
|
+
actions.each do |action|
|
|
15
|
+
action[:data] ||= {}
|
|
16
|
+
action[:data][:turbo_frame] = action[:turbo_frame] if action[:turbo_frame].present?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
{ partial: 'shared/components/table_actions', locals: { row_data:, actions: } }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ViewComponent
|
|
4
|
+
module TypographyComponent
|
|
5
|
+
HEADING_SIZE = {
|
|
6
|
+
'xs' => 'text-base font-semibold',
|
|
7
|
+
'sm' => 'text-lg font-semibold',
|
|
8
|
+
'md' => 'text-xl font-semibold',
|
|
9
|
+
'lg' => 'text-2xl font-semibold',
|
|
10
|
+
'xl' => 'text-3xl font-semibold'
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def merge_class(options, class_names)
|
|
14
|
+
options.merge(class: [*class_names, options[:class]].compact.join(' '))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def link_text_component(text, link, html_options = {})
|
|
18
|
+
render partial: 'view_components/typography/linked_text_component', locals: {
|
|
19
|
+
text:,
|
|
20
|
+
link:,
|
|
21
|
+
html_options:
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param size - xs, sm, md, lg, xl
|
|
26
|
+
def heading_component(text:, size: 'md')
|
|
27
|
+
raise 'BlankValue - heading cannot be blank' if text.blank?
|
|
28
|
+
raise 'IncorrectValue - incorrect size value' unless %w[xs sm md lg xl].include?(size)
|
|
29
|
+
|
|
30
|
+
render partial: 'view_components/typography/heading_component', locals: {
|
|
31
|
+
text:,
|
|
32
|
+
css: HEADING_SIZE[size]
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def h1_component(text: '', html_options: {})
|
|
37
|
+
render partial: 'view_components/typography/h1_component', locals: {
|
|
38
|
+
text:,
|
|
39
|
+
html_options: merge_class(html_options, ['heading-3xl'])
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def h2_component(text: '', html_options: {})
|
|
44
|
+
render partial: 'view_components/typography/h2_component', locals: {
|
|
45
|
+
text:,
|
|
46
|
+
html_options: merge_class(html_options, ['heading-2xl'])
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def h3_component(text: '', html_options: {})
|
|
51
|
+
render partial: 'view_components/typography/h3_component', locals: {
|
|
52
|
+
text:,
|
|
53
|
+
html_options: merge_class(html_options, ['heading-xl'])
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @param text - text to be displayed
|
|
58
|
+
# @param tag - span, p, div
|
|
59
|
+
# @param html_options - additional html options including css classes
|
|
60
|
+
def text_component(text: '', tag: 'span', html_options: {})
|
|
61
|
+
render partial: 'view_components/typography/text_component', locals: {
|
|
62
|
+
text:,
|
|
63
|
+
tag:,
|
|
64
|
+
html_options: merge_class(html_options, ['general-text-md-normal'])
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @param text - text to be displayed
|
|
69
|
+
# @param url - url to be linked
|
|
70
|
+
# @param target - _blank, _self, _parent, _top, framename
|
|
71
|
+
# @param html_options - additional html options
|
|
72
|
+
def link_component(text:, url: '#', target: '_self', html_options: {})
|
|
73
|
+
raise 'BlankValue - text cannot be blank' if text.blank?
|
|
74
|
+
|
|
75
|
+
link_css = ['main-text-sm-normal', 'underline', 'text-primary', 'hover:text-primary-light']
|
|
76
|
+
render partial: 'view_components/typography/link_component', locals: {
|
|
77
|
+
text:,
|
|
78
|
+
url:,
|
|
79
|
+
html_options: merge_class(html_options, link_css).merge(target: target)
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["content", "icon"]
|
|
5
|
+
static values = { open: Boolean }
|
|
6
|
+
|
|
7
|
+
connect() {
|
|
8
|
+
if (this.openValue) {
|
|
9
|
+
this.open()
|
|
10
|
+
} else {
|
|
11
|
+
this.close()
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
toggle(event) {
|
|
16
|
+
if (event.target.closest("a, button")) return
|
|
17
|
+
this.openValue = !this.openValue
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
openValueChanged() {
|
|
21
|
+
if (this.openValue) {
|
|
22
|
+
this.open()
|
|
23
|
+
} else {
|
|
24
|
+
this.close()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
open() {
|
|
29
|
+
this.contentTarget.classList.remove("hidden")
|
|
30
|
+
this.iconTarget.classList.add("rotate-180")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
close() {
|
|
34
|
+
this.contentTarget.classList.add("hidden")
|
|
35
|
+
this.iconTarget.classList.remove("rotate-180")
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["datePicker", "dateSelector"];
|
|
5
|
+
|
|
6
|
+
datePicked(event) {
|
|
7
|
+
const selectedDate = event.target.value;
|
|
8
|
+
|
|
9
|
+
if (selectedDate) {
|
|
10
|
+
this.dateSelectorTarget.value = selectedDate;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
openDatePicker() {
|
|
15
|
+
this.datePickerTarget.showPicker();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = [
|
|
5
|
+
"wrapper",
|
|
6
|
+
"chooseFile",
|
|
7
|
+
"fileInput",
|
|
8
|
+
"selectedFileName",
|
|
9
|
+
"preview",
|
|
10
|
+
"previewContainer",
|
|
11
|
+
"iconPreview",
|
|
12
|
+
"fileName",
|
|
13
|
+
"fileSize"
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
connect() {
|
|
17
|
+
this.fileInputTarget.addEventListener("change", (e) => {
|
|
18
|
+
const file = e.target.files[0];
|
|
19
|
+
this.updateFileName(file);
|
|
20
|
+
this.showPreview(file);
|
|
21
|
+
if (file) this.activate();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
activate() {
|
|
26
|
+
if (!this.isErrorState()) {
|
|
27
|
+
this.wrapperTarget.classList.add("file-selector-is-active");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
deactivate() {
|
|
32
|
+
this.wrapperTarget.classList.remove("file-selector-is-active");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
isErrorState() {
|
|
36
|
+
return this.wrapperTarget.classList.contains("border-danger");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
updateFileName(file) {
|
|
40
|
+
this.clearError();
|
|
41
|
+
this.chooseFileTarget.classList.add("hidden");
|
|
42
|
+
this.selectedFileNameTarget.classList.remove("hidden");
|
|
43
|
+
|
|
44
|
+
if (file) {
|
|
45
|
+
const truncated = this.truncateFileName(file.name, 20);
|
|
46
|
+
this.fileNameTarget.textContent = truncated;
|
|
47
|
+
this.fileSizeTarget.textContent = this.formatFileSize(file.size);
|
|
48
|
+
} else {
|
|
49
|
+
this.fileNameTarget.textContent = "No file chosen";
|
|
50
|
+
this.fileSizeTarget.textContent = "";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
truncateFileName(fileName, maxLength = 10) {
|
|
55
|
+
const dotIndex = fileName.lastIndexOf(".");
|
|
56
|
+
|
|
57
|
+
if (dotIndex === -1) {
|
|
58
|
+
return fileName.length > maxLength
|
|
59
|
+
? fileName.slice(0, maxLength) + "..."
|
|
60
|
+
: fileName;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const namePart = fileName.slice(0, dotIndex);
|
|
64
|
+
const extension = fileName.slice(dotIndex);
|
|
65
|
+
|
|
66
|
+
if (namePart.length <= maxLength) return fileName;
|
|
67
|
+
|
|
68
|
+
return namePart.slice(0, maxLength) + "..." + extension;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
formatFileSize(bytes) {
|
|
72
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
73
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
74
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
showPreview(file) {
|
|
78
|
+
if (!file) return;
|
|
79
|
+
|
|
80
|
+
const fileType = file.type || "";
|
|
81
|
+
const fileName = file.name.toLowerCase();
|
|
82
|
+
this.previewContainerTarget.classList.remove("hidden");
|
|
83
|
+
this.activate();
|
|
84
|
+
|
|
85
|
+
if (this.previewTarget.src.startsWith("blob:")) {
|
|
86
|
+
URL.revokeObjectURL(this.previewTarget.src);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (fileType.startsWith("image/")) {
|
|
90
|
+
const reader = new FileReader();
|
|
91
|
+
reader.onload = () => {
|
|
92
|
+
this.previewTarget.src = reader.result;
|
|
93
|
+
};
|
|
94
|
+
reader.onerror = () => this.showError("Failed to load file preview");
|
|
95
|
+
reader.readAsDataURL(file);
|
|
96
|
+
|
|
97
|
+
} else if (fileType.startsWith("video/") || /\.(mp4|mov|avi|mkv|webm)$/i.test(fileName)) {
|
|
98
|
+
const blobURL = URL.createObjectURL(file);
|
|
99
|
+
this.previewTarget.src = blobURL;
|
|
100
|
+
this.previewTarget.load();
|
|
101
|
+
this.previewTarget.controls = false;
|
|
102
|
+
this.previewTarget.muted = true;
|
|
103
|
+
this.previewTarget.autoplay = false;
|
|
104
|
+
|
|
105
|
+
} else {
|
|
106
|
+
this.previewTarget.src = "";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
showError(message) {
|
|
111
|
+
this.resetInput();
|
|
112
|
+
this.fileNameTarget.innerText = message;
|
|
113
|
+
this.fileNameTarget.style.color = "red";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
clearError() {
|
|
117
|
+
this.fileNameTarget.textContent = "No file chosen";
|
|
118
|
+
this.fileNameTarget.style.color = "";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
resetInput() {
|
|
122
|
+
if (this.hasPreviewTarget && this.previewTarget.src.startsWith("blob:")) {
|
|
123
|
+
URL.revokeObjectURL(this.previewTarget.src);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.fileInputTarget.value = "";
|
|
127
|
+
this.fileNameTarget.textContent = "";
|
|
128
|
+
this.fileSizeTarget.textContent = "";
|
|
129
|
+
this.selectedFileNameTarget.classList.add("hidden");
|
|
130
|
+
this.previewContainerTarget.classList.add("hidden");
|
|
131
|
+
if (this.hasPreviewTarget && this.previewTarget.src) {
|
|
132
|
+
this.previewTarget.src = "";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.chooseFileTarget.classList.remove("hidden");
|
|
136
|
+
this.wrapperTarget.classList.remove("file-selector-is-active");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
chooseFile() {
|
|
140
|
+
if (!this.chooseFileTarget.classList.contains("hidden")) {
|
|
141
|
+
this.activate();
|
|
142
|
+
this.fileInputTarget.click();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["moreMenu","menuButton"];
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
window.addEventListener("click",(e) => {
|
|
8
|
+
if (!this.moreMenuTarget.contains(e.target)) {
|
|
9
|
+
if (!this.isMenuHidden()) {
|
|
10
|
+
this.toggleDropdown(e);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
toggleDropdown(event) {
|
|
17
|
+
event.stopPropagation();
|
|
18
|
+
this.moreMenuTarget.classList.toggle("hidden");
|
|
19
|
+
this.menuButtonTarget.classList.toggle("menu-component-button-active")
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
isMenuHidden() {
|
|
24
|
+
return this.moreMenuTarget.classList.contains("hidden");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["loader"]
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
this.element.addEventListener("submit", this.showLoader.bind(this))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
showLoader(event) {
|
|
11
|
+
this.loaderTarget.classList.remove("hidden")
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["modalBox"];
|
|
5
|
+
|
|
6
|
+
closeModal(event) {
|
|
7
|
+
event.preventDefault();
|
|
8
|
+
this.modalBoxTarget.classList.add("hidden");
|
|
9
|
+
this.modalBoxTarget.parentElement.removeAttribute("src");
|
|
10
|
+
this.modalBoxTarget.parentElement.removeAttribute("complete");
|
|
11
|
+
this.modalBoxTarget.parentElement.innerText = "";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
afterSubmit(event) {
|
|
15
|
+
if (event.detail.success) {
|
|
16
|
+
const fetchResponse = event.detail.fetchResponse;
|
|
17
|
+
history.pushState(
|
|
18
|
+
{ turbo_frame_history: true },
|
|
19
|
+
"",
|
|
20
|
+
fetchResponse.response.url
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
Turbo.visit(fetchResponse.response.url);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {Controller} from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["button"];
|
|
5
|
+
|
|
6
|
+
setPageParam() {
|
|
7
|
+
const nextUrl = this.buttonTarget.getAttribute("href");
|
|
8
|
+
if (!nextUrl) return;
|
|
9
|
+
window.history.replaceState({}, "", nextUrl);
|
|
10
|
+
}
|
|
11
|
+
}
|