panda-core 0.12.5 → 1.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 +4 -4
- data/LICENSE +1 -1
- data/README.md +53 -0
- data/app/assets/builds/panda_core_admin.css +2 -0
- data/app/assets/tailwind/application.css +236 -36
- data/app/builders/panda/core/form_builder.rb +120 -18
- data/app/components/panda/core/UI/badge.html.erb +10 -0
- data/app/components/panda/core/UI/badge.rb +14 -30
- data/app/components/panda/core/UI/button.html.erb +5 -0
- data/app/components/panda/core/UI/button.rb +11 -15
- data/app/components/panda/core/UI/card.html.erb +3 -0
- data/app/components/panda/core/UI/card.rb +7 -6
- data/app/components/panda/core/admin/attribute_diff_component.html.erb +21 -0
- data/app/components/panda/core/admin/attribute_diff_component.rb +29 -0
- data/app/components/panda/core/admin/breadcrumb_component.html.erb +36 -0
- data/app/components/panda/core/admin/breadcrumb_component.rb +12 -64
- data/app/components/panda/core/admin/breadcrumbs_component.html.erb +16 -0
- data/app/components/panda/core/admin/breadcrumbs_component.rb +26 -0
- data/app/components/panda/core/admin/button_component.html.erb +9 -0
- data/app/components/panda/core/admin/button_component.rb +36 -31
- data/app/components/panda/core/admin/callout_component.html.erb +17 -0
- data/app/components/panda/core/admin/callout_component.rb +57 -0
- data/app/components/panda/core/admin/code_block_component.html.erb +9 -0
- data/app/components/panda/core/admin/code_block_component.rb +23 -0
- data/app/components/panda/core/admin/container_component.html.erb +27 -0
- data/app/components/panda/core/admin/container_component.rb +11 -77
- data/app/components/panda/core/admin/contributors_summary_component.html.erb +15 -0
- data/app/components/panda/core/admin/contributors_summary_component.rb +23 -0
- data/app/components/panda/core/admin/dashboard_default_content_component.html.erb +70 -0
- data/app/components/panda/core/admin/dashboard_default_content_component.rb +32 -0
- data/app/components/panda/core/admin/delete_button_component.html.erb +4 -0
- data/app/components/panda/core/admin/delete_button_component.rb +41 -0
- data/app/components/panda/core/admin/dropdown_button_component.html.erb +28 -0
- data/app/components/panda/core/admin/dropdown_button_component.rb +100 -0
- data/app/components/panda/core/admin/dropdown_component.html.erb +32 -0
- data/app/components/panda/core/admin/dropdown_component.rb +65 -0
- data/app/components/panda/core/admin/empty_state_component.html.erb +16 -0
- data/app/components/panda/core/admin/empty_state_component.rb +25 -0
- data/app/components/panda/core/admin/file_gallery_component.html.erb +58 -0
- data/app/components/panda/core/admin/file_gallery_component.rb +26 -98
- data/app/components/panda/core/admin/filter_pills_component.html.erb +6 -0
- data/app/components/panda/core/admin/filter_pills_component.rb +29 -0
- data/app/components/panda/core/admin/flash_message_component.html.erb +11 -0
- data/app/components/panda/core/admin/flash_message_component.rb +33 -60
- data/app/components/panda/core/admin/form_error_component.html.erb +7 -0
- data/app/components/panda/core/admin/form_error_component.rb +8 -16
- data/app/components/panda/core/admin/form_footer_component.html.erb +16 -0
- data/app/components/panda/core/admin/form_footer_component.rb +65 -0
- data/app/components/panda/core/admin/form_input_component.html.erb +1 -0
- data/app/components/panda/core/admin/form_input_component.rb +21 -13
- data/app/components/panda/core/admin/form_section_component.html.erb +16 -0
- data/app/components/panda/core/admin/form_section_component.rb +64 -0
- data/app/components/panda/core/admin/form_select_component.html.erb +5 -0
- data/app/components/panda/core/admin/form_select_component.rb +25 -26
- data/app/components/panda/core/admin/heading_component.html.erb +26 -0
- data/app/components/panda/core/admin/heading_component.rb +23 -60
- data/app/components/panda/core/admin/login_form_component.html.erb +38 -0
- data/app/components/panda/core/admin/login_form_component.rb +28 -0
- data/app/components/panda/core/admin/main_layout_component.html.erb +88 -0
- data/app/components/panda/core/admin/main_layout_component.rb +19 -0
- data/app/components/panda/core/admin/my_profile/connected_account_component.html.erb +41 -0
- data/app/components/panda/core/admin/my_profile/connected_account_component.rb +27 -0
- data/app/components/panda/core/admin/navigation/item_component.html.erb +34 -0
- data/app/components/panda/core/admin/navigation/item_component.rb +69 -0
- data/app/components/panda/core/admin/navigation/sub_item_component.html.erb +9 -0
- data/app/components/panda/core/admin/navigation/sub_item_component.rb +44 -0
- data/app/components/panda/core/admin/navigation/user_menu_component.html.erb +30 -0
- data/app/components/panda/core/admin/navigation/user_menu_component.rb +36 -0
- data/app/components/panda/core/admin/page_header_component.html.erb +28 -0
- data/app/components/panda/core/admin/page_header_component.rb +19 -35
- data/app/components/panda/core/admin/pagination_component.html.erb +33 -0
- data/app/components/panda/core/admin/pagination_component.rb +56 -0
- data/app/components/panda/core/admin/panel_component.html.erb +19 -0
- data/app/components/panda/core/admin/panel_component.rb +4 -29
- data/app/components/panda/core/admin/prose_panel_component.html.erb +6 -0
- data/app/components/panda/core/admin/prose_panel_component.rb +43 -0
- data/app/components/panda/core/admin/search_bar_component.html.erb +19 -0
- data/app/components/panda/core/admin/search_bar_component.rb +31 -0
- data/app/components/panda/core/admin/search_filter_bar_component.html.erb +22 -0
- data/app/components/panda/core/admin/search_filter_bar_component.rb +27 -0
- data/app/components/panda/core/admin/secret_field_component.html.erb +24 -0
- data/app/components/panda/core/admin/secret_field_component.rb +31 -0
- data/app/components/panda/core/admin/sidebar_component.html.erb +160 -0
- data/app/components/panda/core/admin/sidebar_component.rb +55 -0
- data/app/components/panda/core/admin/slideover_component.html.erb +67 -0
- data/app/components/panda/core/admin/slideover_component.rb +31 -70
- data/app/components/panda/core/admin/statistics_component.html.erb +4 -0
- data/app/components/panda/core/admin/statistics_component.rb +6 -8
- data/app/components/panda/core/admin/tab_bar_component.html.erb +48 -0
- data/app/components/panda/core/admin/tab_bar_component.rb +32 -67
- data/app/components/panda/core/admin/table_component.html.erb +82 -0
- data/app/components/panda/core/admin/table_component.rb +54 -87
- data/app/components/panda/core/admin/tag_badge_component.html.erb +7 -0
- data/app/components/panda/core/admin/tag_badge_component.rb +21 -0
- data/app/components/panda/core/admin/tag_component.html.erb +1 -0
- data/app/components/panda/core/admin/tag_component.rb +51 -20
- data/app/components/panda/core/admin/tag_input_component.html.erb +37 -0
- data/app/components/panda/core/admin/tag_input_component.rb +22 -0
- data/app/components/panda/core/admin/tags_panel_component.html.erb +8 -0
- data/app/components/panda/core/admin/tags_panel_component.rb +33 -0
- data/app/components/panda/core/admin/user_activity_component.html.erb +15 -0
- data/app/components/panda/core/admin/user_activity_component.rb +9 -22
- data/app/components/panda/core/admin/user_display_component.html.erb +8 -0
- data/app/components/panda/core/admin/user_display_component.rb +19 -22
- data/app/components/panda/core/base.rb +35 -42
- data/app/components/panda/core/shared/footer_component.html.erb +2 -0
- data/app/components/panda/core/shared/footer_component.rb +11 -0
- data/app/components/panda/core/shared/header_component.html.erb +32 -0
- data/app/components/panda/core/shared/header_component.rb +19 -0
- data/app/constraints/panda/core/admin_constraint.rb +2 -2
- data/app/controllers/concerns/panda/core/admin_authorization.rb +58 -0
- data/app/controllers/concerns/panda/core/authorizable.rb +107 -0
- data/app/controllers/panda/core/admin/base_controller.rb +62 -3
- data/app/controllers/panda/core/admin/editor/search_controller.rb +17 -0
- data/app/controllers/panda/core/admin/feature_flags_controller.rb +35 -0
- data/app/controllers/panda/core/admin/file_categories_controller.rb +84 -0
- data/app/controllers/panda/core/admin/files_controller.rb +230 -0
- data/app/controllers/panda/core/admin/import_sessions_controller.rb +120 -0
- data/app/controllers/panda/core/admin/my_profile/logins_controller.rb +24 -0
- data/app/controllers/panda/core/admin/my_profile_controller.rb +31 -0
- data/app/controllers/panda/core/admin/search_controller.rb +15 -0
- data/app/controllers/panda/core/admin/sessions_controller.rb +84 -10
- data/app/controllers/panda/core/admin/tags_controller.rb +77 -0
- data/app/controllers/panda/core/admin/test_sessions_controller.rb +5 -6
- data/app/controllers/panda/core/admin/users_controller.rb +184 -0
- data/app/controllers/panda/core/application_controller.rb +13 -1
- data/app/helpers/panda/core/active_storage_helper.rb +24 -0
- data/app/helpers/panda/core/feature_flag_helper.rb +11 -0
- data/app/helpers/panda/core/form_helper.rb +15 -0
- data/app/javascript/panda/core/controllers/avatar_upload_controller.js +73 -0
- data/app/javascript/panda/core/controllers/clipboard_controller.js +119 -0
- data/app/javascript/panda/core/controllers/collapsible_item_controller.js +45 -0
- data/app/javascript/panda/core/controllers/custom_select_controller.js +256 -0
- data/app/javascript/panda/core/controllers/datepicker_controller.js +121 -0
- data/app/javascript/panda/core/controllers/daterange_controller.js +193 -0
- data/app/javascript/panda/core/controllers/file_gallery_controller.js +134 -0
- data/app/javascript/panda/core/controllers/file_upload_controller.js +165 -0
- data/app/javascript/panda/core/controllers/global_search_controller.js +184 -0
- data/app/javascript/panda/core/controllers/index.js +36 -0
- data/app/javascript/panda/core/controllers/mobile_sidebar_controller.js +58 -0
- data/app/javascript/panda/core/controllers/navigation_toggle_controller.js +49 -24
- data/app/javascript/panda/core/controllers/row_link_controller.js +27 -0
- data/app/javascript/panda/core/controllers/tag_input_controller.js +183 -0
- data/app/javascript/panda/core/tailwindcss-stimulus-components.js +2 -3
- data/app/javascript/panda/core/vendor/@fortawesome--fontawesome-free@7.2.0.js +8129 -0
- data/app/javascript/panda/core/vendor/@hotwired--turbo.js +7197 -100
- data/app/javascript/panda/core/vendor/@rails--actioncable--src.js +511 -2
- data/app/javascript/panda/core/vendor/@tailwindplus--elements@1.0.22.js +65 -0
- data/app/javascript/panda/core/vendor/cropperjs@2.1.0.js +3 -0
- data/app/javascript/panda/core/vendor/vanilla-calendar-pro.js +2 -0
- data/app/models/concerns/panda/core/has_metadata.rb +164 -0
- data/app/models/concerns/panda/core/importable.rb +79 -0
- data/app/models/concerns/panda/core/searchable.rb +66 -0
- data/app/models/concerns/panda/core/taggable.rb +37 -0
- data/app/models/panda/core/feature_flag.rb +56 -0
- data/app/models/panda/core/file_categorization.rb +14 -0
- data/app/models/panda/core/file_category.rb +45 -0
- data/app/models/panda/core/import_session.rb +79 -0
- data/app/models/panda/core/presence.rb +24 -0
- data/app/models/panda/core/tag.rb +33 -0
- data/app/models/panda/core/tagging.rb +14 -0
- data/app/models/panda/core/user.rb +94 -16
- data/app/models/panda/core/user_activity.rb +42 -0
- data/app/models/panda/core/user_session.rb +35 -0
- data/app/services/panda/core/attach_avatar_service.rb +15 -3
- data/app/services/panda/core/csv_import_service.rb +65 -0
- data/app/services/panda/core/file_categorizer.rb +52 -0
- data/app/services/panda/core/file_parser.rb +76 -0
- data/app/services/panda/core/invite_user_service.rb +44 -0
- data/app/services/panda/core/presence_service.rb +49 -0
- data/app/views/layouts/panda/core/admin.html.erb +6 -76
- data/app/views/layouts/panda/core/admin_simple.html.erb +2 -2
- data/app/views/panda/core/admin/dashboard/_default_content.html.erb +1 -73
- data/app/views/panda/core/admin/dashboard/show.html.erb +12 -15
- data/app/views/panda/core/admin/feature_flags/index.html.erb +56 -0
- data/app/views/panda/core/admin/file_categories/_form.html.erb +20 -0
- data/app/views/panda/core/admin/file_categories/edit.html.erb +5 -0
- data/app/views/panda/core/admin/file_categories/index.html.erb +55 -0
- data/app/views/panda/core/admin/file_categories/new.html.erb +5 -0
- data/app/views/panda/core/admin/files/_file_details.html.erb +156 -0
- data/app/views/panda/core/admin/files/index.html.erb +61 -0
- data/app/views/panda/core/admin/import_sessions/column_map.html.erb +43 -0
- data/app/views/panda/core/admin/import_sessions/index.html.erb +48 -0
- data/app/views/panda/core/admin/import_sessions/new.html.erb +37 -0
- data/app/views/panda/core/admin/import_sessions/preview.html.erb +51 -0
- data/app/views/panda/core/admin/import_sessions/show.html.erb +87 -0
- data/app/views/panda/core/admin/my_profile/edit.html.erb +51 -39
- data/app/views/panda/core/admin/my_profile/logins/show.html.erb +72 -0
- data/app/views/panda/core/admin/sessions/new.html.erb +1 -37
- data/app/views/panda/core/admin/shared/_breadcrumbs.html.erb +1 -18
- data/app/views/panda/core/admin/shared/_sidebar.html.erb +1 -99
- data/app/views/panda/core/admin/shared/_slideover.html.erb +3 -3
- data/app/views/panda/core/admin/tags/_form.html.erb +21 -0
- data/app/views/panda/core/admin/tags/edit.html.erb +4 -0
- data/app/views/panda/core/admin/tags/index.html.erb +41 -0
- data/app/views/panda/core/admin/tags/new.html.erb +4 -0
- data/app/views/panda/core/admin/users/activity.html.erb +90 -0
- data/app/views/panda/core/admin/users/edit.html.erb +48 -0
- data/app/views/panda/core/admin/users/index.html.erb +161 -0
- data/app/views/panda/core/admin/users/sessions.html.erb +103 -0
- data/app/views/panda/core/admin/users/show.html.erb +224 -0
- data/app/views/panda/core/shared/_footer.html.erb +1 -2
- data/app/views/panda/core/shared/_header.html.erb +4 -22
- data/config/brakeman.ignore +68 -31
- data/config/importmap.rb +13 -11
- data/config/routes.rb +49 -2
- data/db/migrate/20260202171614_create_panda_core_file_categories.rb +30 -0
- data/db/migrate/20260203000001_add_user_management_fields_to_panda_core_users.rb +21 -0
- data/db/migrate/20260203000002_create_panda_core_user_activities.rb +24 -0
- data/db/migrate/20260203000003_create_panda_core_user_sessions.rb +25 -0
- data/db/migrate/20260207100000_create_panda_core_presences.rb +21 -0
- data/db/migrate/20260213000001_fix_active_storage_attachments_record_id_type.rb +44 -0
- data/db/migrate/20260217000001_create_panda_core_feature_flags.rb +14 -0
- data/db/migrate/20260303000001_add_metadata_to_panda_core_users.rb +11 -0
- data/db/migrate/20260310000001_create_panda_core_tags.rb +27 -0
- data/db/migrate/20260310000002_create_panda_core_import_sessions.rb +29 -0
- data/lib/generators/panda/core/install_generator.rb +32 -0
- data/lib/generators/panda/core/templates/panda.rb +63 -0
- data/lib/panda/core/asset_loader.rb +5 -2
- data/lib/panda/core/authentication.rb +3 -3
- data/lib/panda/core/configuration.rb +195 -1
- data/lib/panda/core/engine/admin_controller_config.rb +3 -6
- data/lib/panda/core/engine/autoload_config.rb +21 -9
- data/lib/panda/core/engine/omniauth_config.rb +108 -38
- data/lib/panda/core/engine/pending_migrations_check.rb +46 -0
- data/lib/panda/core/engine/sqlite_schema_compatibility.rb +36 -0
- data/lib/panda/core/engine/{phlex_config.rb → view_component_config.rb} +5 -7
- data/lib/panda/core/engine.rb +90 -25
- data/lib/panda/core/middleware.rb +35 -0
- data/lib/panda/core/navigation_registry.rb +455 -0
- data/lib/panda/core/oauth_providers.rb +27 -0
- data/lib/panda/core/permission_registry.rb +89 -0
- data/lib/panda/core/search_registry.rb +69 -0
- data/lib/panda/core/seeds/file_categories.rb +30 -0
- data/lib/panda/core/shared/inflections_config.rb +1 -5
- data/lib/panda/core/testing/rails_helper.rb +87 -0
- data/lib/panda/core/testing/support/authentication_test_helpers.rb +22 -18
- data/lib/panda/core/testing/support/system/cuprite_helpers.rb +4 -4
- data/lib/panda/core/testing/view_component_test_controller.rb +19 -0
- data/lib/panda/core/version.rb +1 -1
- data/lib/panda/core/widget_registry.rb +50 -0
- data/lib/panda/core.rb +17 -8
- data/lib/tasks/panda/core/file_categories.rake +41 -0
- data/public/panda-core-assets/panda-core-0.12.5.css +2 -0
- data/public/panda-core-assets/panda-core-0.13.0.css +2 -0
- data/public/panda-core-assets/panda-core-0.14.0.css +2 -0
- data/public/panda-core-assets/panda-core-0.14.4.css +2 -0
- data/public/panda-core-assets/panda-core.css +2 -2
- data/public/panda-core-assets/vanilla-calendar.css +47 -0
- metadata +203 -26
- data/lib/panda/core/engine/inflections_config.rb +0 -23
|
@@ -27,45 +27,29 @@ module Panda
|
|
|
27
27
|
# )
|
|
28
28
|
#
|
|
29
29
|
class Badge < Panda::Core::Base
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
def initialize(text:, variant: :default, size: :medium, removable: false, rounded: false, **attrs)
|
|
31
|
+
@text = text
|
|
32
|
+
@variant = variant
|
|
33
|
+
@size = size
|
|
34
|
+
@removable = removable
|
|
35
|
+
@rounded = rounded
|
|
36
|
+
super(**attrs)
|
|
37
|
+
end
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
whitespace
|
|
41
|
-
button(
|
|
42
|
-
type: "button",
|
|
43
|
-
class: "inline-flex items-center ml-1 hover:opacity-70",
|
|
44
|
-
aria: {label: "Remove"}
|
|
45
|
-
) do
|
|
46
|
-
svg(
|
|
47
|
-
class: "h-3 w-3",
|
|
48
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
49
|
-
viewBox: "0 0 20 20",
|
|
50
|
-
fill: "currentColor"
|
|
51
|
-
) do |s|
|
|
52
|
-
s.path(
|
|
53
|
-
d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
|
54
|
-
)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
39
|
+
attr_reader :text, :variant, :size, :removable, :rounded
|
|
40
|
+
|
|
41
|
+
def removable?
|
|
42
|
+
removable
|
|
59
43
|
end
|
|
60
44
|
|
|
45
|
+
private
|
|
46
|
+
|
|
61
47
|
def default_attrs
|
|
62
48
|
{
|
|
63
49
|
class: badge_classes
|
|
64
50
|
}
|
|
65
51
|
end
|
|
66
52
|
|
|
67
|
-
private
|
|
68
|
-
|
|
69
53
|
def badge_classes
|
|
70
54
|
base = "inline-flex items-center font-medium"
|
|
71
55
|
base += " #{size_classes}"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Panda
|
|
4
4
|
module Core
|
|
5
5
|
module UI
|
|
6
|
-
# Modern
|
|
6
|
+
# Modern ViewComponent-based button component with type-safe props.
|
|
7
7
|
#
|
|
8
8
|
# Supports both <button> and <a> elements based on whether an href is provided.
|
|
9
9
|
# Follows Tailwind UI Plus styling patterns with dark mode support.
|
|
@@ -34,22 +34,18 @@ module Panda
|
|
|
34
34
|
# )
|
|
35
35
|
#
|
|
36
36
|
class Button < Panda::Core::Base
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def view_template
|
|
46
|
-
if @href
|
|
47
|
-
a(**@attrs) { @text }
|
|
48
|
-
else
|
|
49
|
-
button(**@attrs) { @text }
|
|
50
|
-
end
|
|
37
|
+
def initialize(text: "", variant: :default, size: :medium, disabled: false, type: "button", href: nil, **attrs)
|
|
38
|
+
@text = text
|
|
39
|
+
@variant = variant
|
|
40
|
+
@size = size
|
|
41
|
+
@disabled = disabled
|
|
42
|
+
@type = type
|
|
43
|
+
@href = href
|
|
44
|
+
super(**attrs)
|
|
51
45
|
end
|
|
52
46
|
|
|
47
|
+
attr_reader :text, :variant, :size, :disabled, :type, :href
|
|
48
|
+
|
|
53
49
|
def default_attrs
|
|
54
50
|
base = {
|
|
55
51
|
class: button_classes
|
|
@@ -29,14 +29,15 @@ module Panda
|
|
|
29
29
|
# end
|
|
30
30
|
#
|
|
31
31
|
class Card < Panda::Core::Base
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
div(**@attrs, &block)
|
|
32
|
+
def initialize(padding: :medium, elevation: :low, border: true, **attrs)
|
|
33
|
+
@padding = padding
|
|
34
|
+
@elevation = elevation
|
|
35
|
+
@border = border
|
|
36
|
+
super(**attrs)
|
|
38
37
|
end
|
|
39
38
|
|
|
39
|
+
attr_reader :padding, :elevation, :border
|
|
40
|
+
|
|
40
41
|
def default_attrs
|
|
41
42
|
{
|
|
42
43
|
class: card_classes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%= render Panda::Core::Admin::PanelComponent.new do |panel| %>
|
|
2
|
+
<% panel.with_heading_slot do %>
|
|
3
|
+
<%= heading %>
|
|
4
|
+
<span class="text-sm font-normal text-gray-500 dark:text-gray-400 ml-1">(<%= changes.size %> changed)</span>
|
|
5
|
+
<% end %>
|
|
6
|
+
<% panel.with_body_slot do %>
|
|
7
|
+
<div class="divide-y divide-gray-200 dark:divide-gray-700 -mx-4 -mb-4">
|
|
8
|
+
<% changes.each do |attr, values| %>
|
|
9
|
+
<div class="p-4 grid grid-cols-3 gap-4">
|
|
10
|
+
<div class="text-sm font-medium text-gray-500 dark:text-gray-400"><%= attr.humanize %></div>
|
|
11
|
+
<div class="text-sm">
|
|
12
|
+
<span class="px-1 bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200 line-through"><%= display_value(values[:old]) %></span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="text-sm">
|
|
15
|
+
<span class="px-1 bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200"><%= display_value(values[:new]) %></span>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
</div>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
module Admin
|
|
6
|
+
class AttributeDiffComponent < Panda::Core::Base
|
|
7
|
+
def initialize(changes:, heading: "Attribute Changes", **attrs)
|
|
8
|
+
@changes = changes
|
|
9
|
+
@heading = heading
|
|
10
|
+
super(**attrs)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_reader :changes, :heading
|
|
14
|
+
|
|
15
|
+
def render?
|
|
16
|
+
changes.any?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def display_value(value)
|
|
20
|
+
if value.is_a?(FalseClass) || value.is_a?(TrueClass)
|
|
21
|
+
value.to_s
|
|
22
|
+
else
|
|
23
|
+
value.presence || "(empty)"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<div>
|
|
2
|
+
<%# Mobile back link %>
|
|
3
|
+
<% if @show_back && @items.any? %>
|
|
4
|
+
<nav aria-label="Back" class="sm:hidden">
|
|
5
|
+
<%= content_tag :a, href: back_link_href, class: "flex items-center text-sm font-medium text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300" do %>
|
|
6
|
+
<%= render_chevron_left_icon %>
|
|
7
|
+
Back
|
|
8
|
+
<% end %>
|
|
9
|
+
</nav>
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<%# Desktop breadcrumb trail %>
|
|
13
|
+
<nav aria-label="Breadcrumb" class="hidden sm:flex">
|
|
14
|
+
<ol role="list" class="flex items-center space-x-4">
|
|
15
|
+
<% @items.each_with_index do |item, index| %>
|
|
16
|
+
<li>
|
|
17
|
+
<% if index.zero? %>
|
|
18
|
+
<%# First item (no separator) %>
|
|
19
|
+
<div class="flex">
|
|
20
|
+
<%= content_tag :a, item[:text], href: item[:href], class: breadcrumb_link_classes(index) %>
|
|
21
|
+
</div>
|
|
22
|
+
<% else %>
|
|
23
|
+
<%# Subsequent items (with separator) %>
|
|
24
|
+
<div class="flex items-center">
|
|
25
|
+
<%= render_chevron_right_icon %>
|
|
26
|
+
<%= content_tag :a, item[:text],
|
|
27
|
+
href: item[:href],
|
|
28
|
+
aria: ((index == @items.length - 1) ? {current: "page"} : nil),
|
|
29
|
+
class: breadcrumb_link_classes(index) %>
|
|
30
|
+
</div>
|
|
31
|
+
<% end %>
|
|
32
|
+
</li>
|
|
33
|
+
<% end %>
|
|
34
|
+
</ol>
|
|
35
|
+
</nav>
|
|
36
|
+
</div>
|
|
@@ -26,64 +26,14 @@ module Panda
|
|
|
26
26
|
# )
|
|
27
27
|
#
|
|
28
28
|
class BreadcrumbComponent < Panda::Core::Base
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
div do
|
|
34
|
-
# Mobile back link
|
|
35
|
-
if @show_back && @items.any?
|
|
36
|
-
nav(
|
|
37
|
-
aria: {label: "Back"},
|
|
38
|
-
class: "sm:hidden"
|
|
39
|
-
) do
|
|
40
|
-
a(
|
|
41
|
-
href: back_link_href,
|
|
42
|
-
class: "flex items-center text-sm font-medium text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
|
43
|
-
) do
|
|
44
|
-
render_chevron_left_icon
|
|
45
|
-
plain "Back"
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Desktop breadcrumb trail
|
|
51
|
-
nav(
|
|
52
|
-
aria: {label: "Breadcrumb"},
|
|
53
|
-
class: "hidden sm:flex"
|
|
54
|
-
) do
|
|
55
|
-
ol(
|
|
56
|
-
role: "list",
|
|
57
|
-
class: "flex items-center space-x-4"
|
|
58
|
-
) do
|
|
59
|
-
@items.each_with_index do |item, index|
|
|
60
|
-
li do
|
|
61
|
-
if index.zero?
|
|
62
|
-
# First item (no separator)
|
|
63
|
-
div(class: "flex") do
|
|
64
|
-
a(
|
|
65
|
-
href: item[:href],
|
|
66
|
-
class: breadcrumb_link_classes(index)
|
|
67
|
-
) { item[:text] }
|
|
68
|
-
end
|
|
69
|
-
else
|
|
70
|
-
# Subsequent items (with separator)
|
|
71
|
-
div(class: "flex items-center") do
|
|
72
|
-
render_chevron_right_icon
|
|
73
|
-
a(
|
|
74
|
-
href: item[:href],
|
|
75
|
-
aria: ((index == @items.length - 1) ? {current: "page"} : nil),
|
|
76
|
-
class: breadcrumb_link_classes(index)
|
|
77
|
-
) { item[:text] }
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
29
|
+
def initialize(items: [], show_back: true, **attrs)
|
|
30
|
+
@items = items
|
|
31
|
+
@show_back = show_back
|
|
32
|
+
super(**attrs)
|
|
85
33
|
end
|
|
86
34
|
|
|
35
|
+
attr_reader :items, :show_back
|
|
36
|
+
|
|
87
37
|
private
|
|
88
38
|
|
|
89
39
|
def back_link_href
|
|
@@ -97,14 +47,13 @@ module Panda
|
|
|
97
47
|
end
|
|
98
48
|
|
|
99
49
|
def render_chevron_left_icon
|
|
100
|
-
svg
|
|
50
|
+
content_tag(:svg,
|
|
101
51
|
viewBox: "0 0 20 20",
|
|
102
52
|
fill: "currentColor",
|
|
103
53
|
data: {slot: "icon"},
|
|
104
54
|
aria: {hidden: "true"},
|
|
105
|
-
class: "mr-1 -ml-1 size-5 shrink-0 text-gray-400 dark:text-gray-500"
|
|
106
|
-
|
|
107
|
-
s.path(
|
|
55
|
+
class: "mr-1 -ml-1 size-5 shrink-0 text-gray-400 dark:text-gray-500") do
|
|
56
|
+
tag.path(
|
|
108
57
|
d: "M11.78 5.22a.75.75 0 0 1 0 1.06L8.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z",
|
|
109
58
|
clip_rule: "evenodd",
|
|
110
59
|
fill_rule: "evenodd"
|
|
@@ -113,14 +62,13 @@ module Panda
|
|
|
113
62
|
end
|
|
114
63
|
|
|
115
64
|
def render_chevron_right_icon
|
|
116
|
-
svg
|
|
65
|
+
content_tag(:svg,
|
|
117
66
|
viewBox: "0 0 20 20",
|
|
118
67
|
fill: "currentColor",
|
|
119
68
|
data: {slot: "icon"},
|
|
120
69
|
aria: {hidden: "true"},
|
|
121
|
-
class: "size-5 shrink-0 text-gray-400 dark:text-gray-500"
|
|
122
|
-
|
|
123
|
-
s.path(
|
|
70
|
+
class: "size-5 shrink-0 text-gray-400 dark:text-gray-500") do
|
|
71
|
+
tag.path(
|
|
124
72
|
d: "M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z",
|
|
125
73
|
clip_rule: "evenodd",
|
|
126
74
|
fill_rule: "evenodd"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<nav aria-label="Breadcrumb" id="panda-breadcrumbs" <% if @attrs.any? %><%= tag.attributes(@attrs) %><% end %>>
|
|
2
|
+
<ol role="list">
|
|
3
|
+
<li class="inline-block">
|
|
4
|
+
<a href="<%= Panda::Core.config.admin_path %>" class="text-black/60 hover:text-black/80">
|
|
5
|
+
<i class="fa fa-solid fa-house fa-fw inline-block w-4 text-center"></i>
|
|
6
|
+
<span class="sr-only">Home</span>
|
|
7
|
+
</a>
|
|
8
|
+
</li>
|
|
9
|
+
<% @breadcrumbs.each do |crumb| %>
|
|
10
|
+
<li class="inline-block">
|
|
11
|
+
<i class="fa-solid fa-chevron-right fa-fw inline-block w-4 text-center font-light text-black/60 hover:text-black/80 px-2"></i>
|
|
12
|
+
<a href="<%= crumb.path %>" class="text-sm font-normal text-black/60 hover:text-black/80"><%= crumb.name %></a>
|
|
13
|
+
</li>
|
|
14
|
+
<% end %>
|
|
15
|
+
</ol>
|
|
16
|
+
</nav>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
module Admin
|
|
6
|
+
# Breadcrumbs navigation component for admin pages
|
|
7
|
+
# Displays hierarchical navigation starting from admin home
|
|
8
|
+
class BreadcrumbsComponent < Panda::Core::Base
|
|
9
|
+
def initialize(breadcrumbs: [], **attrs)
|
|
10
|
+
@breadcrumbs = breadcrumbs
|
|
11
|
+
super(**attrs)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :breadcrumbs
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def default_attrs
|
|
19
|
+
{
|
|
20
|
+
class: "px-4 w-full sm:px-6 py-2.5"
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -4,26 +4,19 @@ module Panda
|
|
|
4
4
|
module Core
|
|
5
5
|
module Admin
|
|
6
6
|
class ButtonComponent < Panda::Core::Base
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if @as_button
|
|
17
|
-
button(**@attrs) do
|
|
18
|
-
render_content
|
|
19
|
-
end
|
|
20
|
-
else
|
|
21
|
-
a(**@attrs) do
|
|
22
|
-
render_content
|
|
23
|
-
end
|
|
24
|
-
end
|
|
7
|
+
def initialize(text: "Button", action: nil, href: "#", icon: nil, size: :regular, id: nil, as_button: false, **attrs)
|
|
8
|
+
@text = text
|
|
9
|
+
@action = action
|
|
10
|
+
@href = href
|
|
11
|
+
@icon = icon
|
|
12
|
+
@size = size
|
|
13
|
+
@id = id
|
|
14
|
+
@as_button = as_button
|
|
15
|
+
super(**attrs)
|
|
25
16
|
end
|
|
26
17
|
|
|
18
|
+
attr_reader :text, :action, :href, :icon, :size, :id, :as_button
|
|
19
|
+
|
|
27
20
|
def default_attrs
|
|
28
21
|
base = {
|
|
29
22
|
class: button_classes,
|
|
@@ -40,10 +33,22 @@ module Panda
|
|
|
40
33
|
private
|
|
41
34
|
|
|
42
35
|
def render_content
|
|
43
|
-
if computed_icon
|
|
44
|
-
i
|
|
36
|
+
icon_html = if computed_icon
|
|
37
|
+
content_tag(:i, "", class: "fa-solid fa-#{computed_icon}")
|
|
38
|
+
else
|
|
39
|
+
"".html_safe
|
|
45
40
|
end
|
|
46
|
-
|
|
41
|
+
text_html = @text.titleize
|
|
42
|
+
|
|
43
|
+
if icon_right?
|
|
44
|
+
(text_html + icon_html).html_safe
|
|
45
|
+
else
|
|
46
|
+
(icon_html + text_html).html_safe
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def icon_right?
|
|
51
|
+
@action.in?(%i[save create add new])
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
def computed_icon
|
|
@@ -51,35 +56,35 @@ module Panda
|
|
|
51
56
|
end
|
|
52
57
|
|
|
53
58
|
def button_classes
|
|
54
|
-
base = "inline-flex items-center rounded-
|
|
59
|
+
base = "inline-flex items-center rounded-xl font-medium shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 cursor-pointer transition-colors "
|
|
55
60
|
base + size_classes + action_classes
|
|
56
61
|
end
|
|
57
62
|
|
|
58
63
|
def size_classes
|
|
59
64
|
case @size
|
|
60
65
|
when :small, :sm
|
|
61
|
-
"gap-x-1.5 px-
|
|
66
|
+
"gap-x-1.5 px-3 py-1.5 text-xs "
|
|
62
67
|
when :medium, :regular, :md
|
|
63
|
-
"gap-x-
|
|
68
|
+
"gap-x-2 px-4 py-2 text-sm "
|
|
64
69
|
when :large, :lg
|
|
65
|
-
"gap-x-2 px-
|
|
70
|
+
"gap-x-2 px-5 py-2.5 text-base "
|
|
66
71
|
else
|
|
67
|
-
"gap-x-
|
|
72
|
+
"gap-x-2 px-4 py-2 text-sm "
|
|
68
73
|
end
|
|
69
74
|
end
|
|
70
75
|
|
|
71
76
|
def action_classes
|
|
72
77
|
case @action
|
|
73
|
-
when :save, :create
|
|
78
|
+
when :save, :create, :add, :new
|
|
74
79
|
"text-white bg-primary-500 hover:bg-primary-600 focus-visible:outline-primary-600"
|
|
75
80
|
when :save_inactive
|
|
76
|
-
"text-white bg-gray-
|
|
81
|
+
"text-white bg-gray-300 cursor-not-allowed"
|
|
77
82
|
when :secondary
|
|
78
|
-
"text-gray-700 border
|
|
83
|
+
"text-gray-700 border border-gray-200 bg-white hover:bg-gray-50"
|
|
79
84
|
when :delete, :destroy, :danger
|
|
80
|
-
"text-error-600 border border-error-
|
|
85
|
+
"text-error-600 border border-error-200 bg-error-50 hover:bg-error-100 focus-visible:outline-error-300"
|
|
81
86
|
else
|
|
82
|
-
"text-gray-700 border
|
|
87
|
+
"text-gray-700 border border-gray-200 bg-white hover:bg-gray-50"
|
|
83
88
|
end
|
|
84
89
|
end
|
|
85
90
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%= content_tag :div, **attrs do %>
|
|
2
|
+
<div class="flex items-start gap-3">
|
|
3
|
+
<i class="fa-solid <%= icon_class %> mt-0.5"></i>
|
|
4
|
+
<div>
|
|
5
|
+
<% if title.present? %>
|
|
6
|
+
<div class="text-sm font-medium"><%= title %></div>
|
|
7
|
+
<% end %>
|
|
8
|
+
<div class="text-sm">
|
|
9
|
+
<% if content? %>
|
|
10
|
+
<%= content %>
|
|
11
|
+
<% else %>
|
|
12
|
+
<%= text %>
|
|
13
|
+
<% end %>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<% end %>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
module Admin
|
|
6
|
+
# Lightweight callout for inline notices (info, warning, success, error).
|
|
7
|
+
# Intended for static, in-form guidance (not toast/flash).
|
|
8
|
+
class CalloutComponent < Panda::Core::Base
|
|
9
|
+
def initialize(text: nil, title: nil, kind: :info, icon: nil, **attrs)
|
|
10
|
+
@text = text
|
|
11
|
+
@title = title
|
|
12
|
+
@kind = kind
|
|
13
|
+
@icon = icon
|
|
14
|
+
super(**attrs)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :text, :title, :kind, :icon
|
|
18
|
+
|
|
19
|
+
def default_attrs
|
|
20
|
+
{
|
|
21
|
+
class: "rounded-2xl border px-4 py-3 text-sm #{tone_classes}"
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def icon_class
|
|
26
|
+
icon || default_icon
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def default_icon
|
|
30
|
+
case kind
|
|
31
|
+
when :success
|
|
32
|
+
"fa-circle-check"
|
|
33
|
+
when :warning
|
|
34
|
+
"fa-triangle-exclamation"
|
|
35
|
+
when :error, :alert
|
|
36
|
+
"fa-circle-xmark"
|
|
37
|
+
else
|
|
38
|
+
"fa-circle-info"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def tone_classes
|
|
43
|
+
case kind
|
|
44
|
+
when :success
|
|
45
|
+
"bg-emerald-50 text-emerald-700 border-emerald-200"
|
|
46
|
+
when :warning
|
|
47
|
+
"bg-amber-50 text-amber-700 border-amber-200"
|
|
48
|
+
when :error, :alert
|
|
49
|
+
"bg-rose-50 text-rose-700 border-rose-200"
|
|
50
|
+
else
|
|
51
|
+
"bg-gray-50 text-gray-700 border-gray-200"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Panda
|
|
4
|
+
module Core
|
|
5
|
+
module Admin
|
|
6
|
+
# Code block component for snippets and examples.
|
|
7
|
+
class CodeBlockComponent < Panda::Core::Base
|
|
8
|
+
def initialize(code: nil, **attrs)
|
|
9
|
+
@code = code
|
|
10
|
+
super(**attrs)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_reader :code
|
|
14
|
+
|
|
15
|
+
def default_attrs
|
|
16
|
+
{
|
|
17
|
+
class: "rounded-xl bg-gray-900/95 p-3 text-xs text-emerald-200 font-mono overflow-x-auto"
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<div class="relative" data-controller="toggle">
|
|
2
|
+
<main class="overflow-auto flex-1 h-full min-h-full max-h-full">
|
|
3
|
+
<div class="overflow-auto px-2 pt-2 mx-auto sm:px-6 lg:px-6">
|
|
4
|
+
<% if heading_slot? %>
|
|
5
|
+
<%= heading_slot %>
|
|
6
|
+
<% end %>
|
|
7
|
+
|
|
8
|
+
<% if tab_bar_slot? %>
|
|
9
|
+
<%= tab_bar_slot %>
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<section class="<%= section_classes %>">
|
|
13
|
+
<div class="flex-1 mt-4 w-full h-full">
|
|
14
|
+
<% if body_slot? %>
|
|
15
|
+
<%= body_slot %>
|
|
16
|
+
<% elsif content.present? %>
|
|
17
|
+
<%= content %>
|
|
18
|
+
<% end %>
|
|
19
|
+
</div>
|
|
20
|
+
</section>
|
|
21
|
+
</div>
|
|
22
|
+
</main>
|
|
23
|
+
|
|
24
|
+
<% if slideover_slot? %>
|
|
25
|
+
<%= slideover_slot %>
|
|
26
|
+
<% end %>
|
|
27
|
+
</div>
|