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
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
@import 'tailwindcss';
|
|
2
2
|
|
|
3
3
|
@theme {
|
|
4
|
+
/* Custom spacing for page tree indentation */
|
|
5
|
+
--spacing-18: 4.5rem;
|
|
6
|
+
|
|
4
7
|
/* Legacy colors - DEPRECATED: Use primary-* scale instead */
|
|
5
8
|
/* Fallback values ensure UI is visible even without data-theme attribute */
|
|
6
9
|
--color-white: #f9f9f9;
|
|
@@ -30,6 +33,8 @@
|
|
|
30
33
|
/* Semantic colors - Success (Green) */
|
|
31
34
|
--color-success-50: #f0fdf4;
|
|
32
35
|
--color-success-100: #dcfce7;
|
|
36
|
+
--color-success-200: #bbf7d0;
|
|
37
|
+
--color-success-300: #86efac;
|
|
33
38
|
--color-success-500: #22c55e;
|
|
34
39
|
--color-success-600: #16a34a;
|
|
35
40
|
--color-success-700: #15803d;
|
|
@@ -37,6 +42,8 @@
|
|
|
37
42
|
/* Semantic colors - Error (Red) */
|
|
38
43
|
--color-error-50: #fef2f2;
|
|
39
44
|
--color-error-100: #fee2e2;
|
|
45
|
+
--color-error-200: #fecaca;
|
|
46
|
+
--color-error-300: #fca5a5;
|
|
40
47
|
--color-error-500: #ef4444;
|
|
41
48
|
--color-error-600: #dc2626;
|
|
42
49
|
--color-error-700: #b91c1c;
|
|
@@ -115,6 +122,8 @@
|
|
|
115
122
|
/* Semantic colors - Success (Green) */
|
|
116
123
|
--color-success-50: rgb(240, 253, 244); /* green-50 */
|
|
117
124
|
--color-success-100: rgb(220, 252, 231); /* green-100 */
|
|
125
|
+
--color-success-200: rgb(187, 247, 208); /* green-200 */
|
|
126
|
+
--color-success-300: rgb(134, 239, 172); /* green-300 */
|
|
118
127
|
--color-success-500: rgb(34, 197, 94); /* green-500 */
|
|
119
128
|
--color-success-600: rgb(22, 163, 74); /* green-600 */
|
|
120
129
|
--color-success-700: rgb(21, 128, 61); /* green-700 */
|
|
@@ -122,6 +131,8 @@
|
|
|
122
131
|
/* Semantic colors - Error (Red) */
|
|
123
132
|
--color-error-50: rgb(254, 242, 242); /* red-50 */
|
|
124
133
|
--color-error-100: rgb(254, 226, 226); /* red-100 */
|
|
134
|
+
--color-error-200: rgb(254, 202, 202); /* red-200 */
|
|
135
|
+
--color-error-300: rgb(252, 165, 165); /* red-300 */
|
|
125
136
|
--color-error-500: rgb(239, 68, 68); /* red-500 */
|
|
126
137
|
--color-error-600: rgb(220, 38, 38); /* red-600 */
|
|
127
138
|
--color-error-700: rgb(185, 28, 28); /* red-700 */
|
|
@@ -152,9 +163,55 @@
|
|
|
152
163
|
--color-gray-800: rgb(31, 41, 55);
|
|
153
164
|
--color-gray-900: rgb(17, 24, 39);
|
|
154
165
|
--color-gray-950: rgb(3, 7, 18);
|
|
166
|
+
|
|
167
|
+
/*
|
|
168
|
+
* Panda admin chrome tokens — opt-in theming for the residual gem-rendered
|
|
169
|
+
* admin surfaces (PanelComponent, TableComponent, TagComponent badges, form
|
|
170
|
+
* inputs, the custom-select trigger, checkbox accent). Every value below is
|
|
171
|
+
* a var() reference to an existing token, so it resolves to exactly today's
|
|
172
|
+
* literal colour — this block changes nothing visually for the default
|
|
173
|
+
* theme. Host apps (e.g. Panda CMS) that never set a theme, or that use
|
|
174
|
+
* "default"/"sky", are unaffected. A host app can override any of these
|
|
175
|
+
* tokens directly instead of adopting a whole new html[data-theme] block.
|
|
176
|
+
*/
|
|
177
|
+
--panda-panel-bg: var(--color-white);
|
|
178
|
+
--panda-panel-border: var(--color-gray-200);
|
|
179
|
+
--panda-panel-radius: var(--radius-2xl);
|
|
180
|
+
--panda-table-header-bg: var(--color-gray-50);
|
|
181
|
+
--panda-table-border: var(--color-gray-200);
|
|
182
|
+
--panda-table-row-border: color-mix(in srgb, var(--color-gray-200) 70%, transparent);
|
|
183
|
+
--panda-table-cell-color: var(--color-gray-600);
|
|
184
|
+
--panda-badge-success-bg: var(--color-emerald-50);
|
|
185
|
+
--panda-badge-success-fg: var(--color-emerald-600);
|
|
186
|
+
--panda-badge-warning-bg: var(--color-amber-50);
|
|
187
|
+
--panda-badge-warning-fg: var(--color-amber-600);
|
|
188
|
+
--panda-badge-info-bg: var(--color-sky-50);
|
|
189
|
+
--panda-badge-info-fg: var(--color-sky-600);
|
|
190
|
+
--panda-badge-error-bg: var(--color-rose-50);
|
|
191
|
+
--panda-badge-error-fg: var(--color-rose-600);
|
|
192
|
+
--panda-badge-neutral-bg: var(--color-gray-100);
|
|
193
|
+
--panda-badge-neutral-fg: var(--color-gray-600);
|
|
194
|
+
--panda-input-border: var(--color-gray-300);
|
|
195
|
+
--panda-input-radius: var(--radius-xl);
|
|
196
|
+
--panda-input-focus-ring: var(--color-primary-500);
|
|
197
|
+
--panda-select-trigger-border: var(--color-gray-200);
|
|
198
|
+
--panda-checkbox-accent: var(--color-primary-500);
|
|
155
199
|
}
|
|
156
200
|
|
|
157
|
-
|
|
201
|
+
/*
|
|
202
|
+
* NAMING NOTE (2026-07): this block was briefly named `html[data-theme='ocean']`
|
|
203
|
+
* (commit aa7f021, "Update color palette to Glacier/Teal theme"), diverging from
|
|
204
|
+
* `Panda::Core.config.available_themes`, which has always shipped
|
|
205
|
+
* `[["Default", "default"], ["Sky", "sky"]]` (since the auth migration from
|
|
206
|
+
* panda-cms). Since `available_themes` populates the theme <select> in
|
|
207
|
+
* my_profile/edit.html.erb, any user who picked the second theme before aa7f021
|
|
208
|
+
* — or since, via a host app that left available_themes at its default — has
|
|
209
|
+
* "sky" persisted in `current_theme`, not "ocean". Renaming the CSS block back
|
|
210
|
+
* to `sky` (rather than renaming the config to `ocean`) restores that match
|
|
211
|
+
* without silently losing anyone's saved preference. Do not rename this block
|
|
212
|
+
* without also migrating `Panda::Core::User#current_theme` data.
|
|
213
|
+
*/
|
|
214
|
+
html[data-theme='sky'] {
|
|
158
215
|
--color-white: rgb(249, 249, 249); /* #F9F9F9 */
|
|
159
216
|
--color-black: rgb(26, 22, 29); /* #1A161D */
|
|
160
217
|
|
|
@@ -170,7 +227,7 @@
|
|
|
170
227
|
--color-inactive: rgb(219, 234, 254); /* blue-100 */
|
|
171
228
|
--color-error: rgb(220, 38, 38); /* red-600 */
|
|
172
229
|
|
|
173
|
-
/* Primary color scale -
|
|
230
|
+
/* Primary color scale - Sky (Blue) */
|
|
174
231
|
--color-primary-50: rgb(239, 246, 255); /* blue-50 */
|
|
175
232
|
--color-primary-100: rgb(219, 234, 254); /* blue-100 */
|
|
176
233
|
--color-primary-200: rgb(191, 219, 254); /* blue-200 */
|
|
@@ -186,6 +243,8 @@
|
|
|
186
243
|
/* Semantic colors - Success (Green) */
|
|
187
244
|
--color-success-50: rgb(240, 253, 244);
|
|
188
245
|
--color-success-100: rgb(220, 252, 231);
|
|
246
|
+
--color-success-200: rgb(187, 247, 208);
|
|
247
|
+
--color-success-300: rgb(134, 239, 172);
|
|
189
248
|
--color-success-500: rgb(34, 197, 94);
|
|
190
249
|
--color-success-600: rgb(22, 163, 74);
|
|
191
250
|
--color-success-700: rgb(21, 128, 61);
|
|
@@ -193,6 +252,8 @@
|
|
|
193
252
|
/* Semantic colors - Error (Red) */
|
|
194
253
|
--color-error-50: rgb(254, 242, 242);
|
|
195
254
|
--color-error-100: rgb(254, 226, 226);
|
|
255
|
+
--color-error-200: rgb(254, 202, 202);
|
|
256
|
+
--color-error-300: rgb(252, 165, 165);
|
|
196
257
|
--color-error-500: rgb(239, 68, 68);
|
|
197
258
|
--color-error-600: rgb(220, 38, 38);
|
|
198
259
|
--color-error-700: rgb(185, 28, 28);
|
|
@@ -204,7 +265,7 @@
|
|
|
204
265
|
--color-warning-600: rgb(217, 119, 6);
|
|
205
266
|
--color-warning-700: rgb(180, 83, 9);
|
|
206
267
|
|
|
207
|
-
/* Semantic colors - Info (Teal - different from primary for
|
|
268
|
+
/* Semantic colors - Info (Teal - different from primary for sky theme) */
|
|
208
269
|
--color-info-50: rgb(241, 249, 250);
|
|
209
270
|
--color-info-100: rgb(219, 238, 242);
|
|
210
271
|
--color-info-500: rgb(60, 135, 158);
|
|
@@ -223,19 +284,69 @@
|
|
|
223
284
|
--color-gray-800: rgb(31, 41, 55);
|
|
224
285
|
--color-gray-900: rgb(17, 24, 39);
|
|
225
286
|
--color-gray-950: rgb(3, 7, 18);
|
|
226
|
-
}
|
|
227
287
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
288
|
+
/* Panda admin chrome tokens — see html[data-theme='default'] for the
|
|
289
|
+
full explanation. Same literal colours as default: these are
|
|
290
|
+
theme-agnostic chrome tokens, unaffected by the sky/default primary
|
|
291
|
+
colour split. */
|
|
292
|
+
--panda-panel-bg: var(--color-white);
|
|
293
|
+
--panda-panel-border: var(--color-gray-200);
|
|
294
|
+
--panda-panel-radius: var(--radius-2xl);
|
|
295
|
+
--panda-table-header-bg: var(--color-gray-50);
|
|
296
|
+
--panda-table-border: var(--color-gray-200);
|
|
297
|
+
--panda-table-row-border: color-mix(in srgb, var(--color-gray-200) 70%, transparent);
|
|
298
|
+
--panda-table-cell-color: var(--color-gray-600);
|
|
299
|
+
--panda-badge-success-bg: var(--color-emerald-50);
|
|
300
|
+
--panda-badge-success-fg: var(--color-emerald-600);
|
|
301
|
+
--panda-badge-warning-bg: var(--color-amber-50);
|
|
302
|
+
--panda-badge-warning-fg: var(--color-amber-600);
|
|
303
|
+
--panda-badge-info-bg: var(--color-sky-50);
|
|
304
|
+
--panda-badge-info-fg: var(--color-sky-600);
|
|
305
|
+
--panda-badge-error-bg: var(--color-rose-50);
|
|
306
|
+
--panda-badge-error-fg: var(--color-rose-600);
|
|
307
|
+
--panda-badge-neutral-bg: var(--color-gray-100);
|
|
308
|
+
--panda-badge-neutral-fg: var(--color-gray-600);
|
|
309
|
+
--panda-input-border: var(--color-gray-300);
|
|
310
|
+
--panda-input-radius: var(--radius-xl);
|
|
311
|
+
--panda-input-focus-ring: var(--color-primary-500);
|
|
312
|
+
--panda-select-trigger-border: var(--color-gray-200);
|
|
313
|
+
--panda-checkbox-accent: var(--color-primary-500);
|
|
232
314
|
}
|
|
233
315
|
|
|
234
|
-
/*
|
|
316
|
+
/*
|
|
317
|
+
* Bring-your-own theme: host apps can supply additional theme blocks in
|
|
318
|
+
* their own stylesheets — the gem does not need to know about them. A
|
|
319
|
+
* consuming app:
|
|
320
|
+
*
|
|
321
|
+
* 1. Registers the theme so it appears in the My Profile dropdown and
|
|
322
|
+
* can be the default:
|
|
323
|
+
* config.available_themes = [["Default", "default"], ["Acme", "acme"]]
|
|
324
|
+
* config.default_theme = "acme"
|
|
325
|
+
* (assigning available_themes REPLACES the gem's default list)
|
|
326
|
+
* 2. Ships a stylesheet defining the variables, loaded on gem-rendered
|
|
327
|
+
* pages via config.additional_head_content (rendered in the <head>
|
|
328
|
+
* of every admin layout, including the login page):
|
|
329
|
+
* html[data-theme='acme'] {
|
|
330
|
+
* --color-primary-500: ...; /- brand scale, if desired -/
|
|
331
|
+
* --gradient-admin-from: ...; /- sidebar/login gradient -/
|
|
332
|
+
* --panda-panel-bg: ...; /- any of the chrome tokens -/
|
|
333
|
+
* }
|
|
334
|
+
*
|
|
335
|
+
* HeaderComponent stamps data-theme from Current.user&.current_theme ||
|
|
336
|
+
* config.default_theme on <html>, so the block activates automatically.
|
|
337
|
+
* Any token left undefined falls back to the gem default.
|
|
338
|
+
*/
|
|
339
|
+
|
|
340
|
+
/* Admin gradient backgrounds - customizable via CSS custom properties */
|
|
235
341
|
.bg-gradient-admin {
|
|
236
|
-
background: linear-gradient(to bottom right, rgb(17, 24, 39), rgb(55, 65, 81));
|
|
342
|
+
background: linear-gradient(to bottom right, var(--gradient-admin-from, rgb(17, 24, 39)), var(--gradient-admin-to, rgb(55, 65, 81)));
|
|
237
343
|
border-bottom-left-radius: 0;
|
|
238
344
|
}
|
|
345
|
+
|
|
346
|
+
/* Clickable table rows containing a block-link */
|
|
347
|
+
.table-row:has(a.block-link) {
|
|
348
|
+
cursor: pointer;
|
|
349
|
+
}
|
|
239
350
|
}
|
|
240
351
|
|
|
241
352
|
/* Form input styles */
|
|
@@ -252,33 +363,61 @@
|
|
|
252
363
|
input[type='month'],
|
|
253
364
|
input[type='week'],
|
|
254
365
|
input[type='time'],
|
|
255
|
-
input[type='search']
|
|
366
|
+
input[type='search'] {
|
|
367
|
+
@apply block w-full h-11 rounded-xl border border-gray-300 px-3 py-2 text-gray-900;
|
|
368
|
+
@apply placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-primary-500;
|
|
369
|
+
@apply dark:bg-white/5 dark:text-white dark:border-white/10 dark:placeholder:text-gray-500;
|
|
370
|
+
@apply disabled:border-gray-200 disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed;
|
|
371
|
+
background-color: #fff;
|
|
372
|
+
/* Themeable border/radius/focus-ring — see --panda-input-* tokens above.
|
|
373
|
+
Fallbacks match the @apply values on this rule exactly, so this is a
|
|
374
|
+
no-op for consumers that never set a data-theme. */
|
|
375
|
+
border-color: var(--panda-input-border, var(--color-gray-300));
|
|
376
|
+
border-radius: var(--panda-input-radius, var(--radius-xl));
|
|
377
|
+
|
|
378
|
+
&:focus {
|
|
379
|
+
outline-color: var(--panda-input-focus-ring, var(--color-primary-500));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
256
383
|
textarea {
|
|
257
|
-
@apply block w-full rounded-
|
|
258
|
-
@apply
|
|
259
|
-
@apply
|
|
260
|
-
@apply
|
|
261
|
-
|
|
384
|
+
@apply block w-full rounded-xl border border-gray-300 px-3 py-2 text-gray-900;
|
|
385
|
+
@apply placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-primary-500;
|
|
386
|
+
@apply dark:bg-white/5 dark:text-white dark:border-white/10 dark:placeholder:text-gray-500;
|
|
387
|
+
@apply disabled:border-gray-200 disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed;
|
|
388
|
+
background-color: #fff;
|
|
389
|
+
border-color: var(--panda-input-border, var(--color-gray-300));
|
|
390
|
+
border-radius: var(--panda-input-radius, var(--radius-xl));
|
|
391
|
+
|
|
392
|
+
&:focus {
|
|
393
|
+
outline-color: var(--panda-input-focus-ring, var(--color-primary-500));
|
|
394
|
+
}
|
|
262
395
|
}
|
|
263
396
|
|
|
264
397
|
/* Select specific styling */
|
|
265
398
|
select {
|
|
266
|
-
@apply block w-full rounded-
|
|
267
|
-
@apply
|
|
268
|
-
@apply
|
|
269
|
-
@apply
|
|
270
|
-
@apply disabled:ring-gray-300 disabled:focus:ring-gray-300 disabled:bg-gray-50 disabled:cursor-not-allowed;
|
|
399
|
+
@apply block w-full h-11 rounded-xl border border-gray-300 px-3 py-2 text-gray-900;
|
|
400
|
+
@apply focus:outline-2 focus:-outline-offset-2 focus:outline-primary-500;
|
|
401
|
+
@apply dark:bg-white/5 dark:text-white dark:border-white/10;
|
|
402
|
+
@apply disabled:border-gray-200 disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed;
|
|
271
403
|
@apply appearance-none bg-right bg-no-repeat;
|
|
404
|
+
background-color: #fff;
|
|
272
405
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
|
273
|
-
background-position: right 0.
|
|
274
|
-
background-size: 1.
|
|
406
|
+
background-position: right 0.75rem center;
|
|
407
|
+
background-size: 1.25em 1.25em;
|
|
408
|
+
border-color: var(--panda-input-border, var(--color-gray-300));
|
|
409
|
+
border-radius: var(--panda-input-radius, var(--radius-xl));
|
|
410
|
+
|
|
411
|
+
&:focus {
|
|
412
|
+
outline-color: var(--panda-input-focus-ring, var(--color-primary-500));
|
|
413
|
+
}
|
|
275
414
|
}
|
|
276
415
|
|
|
277
416
|
/* Checkbox and radio styling */
|
|
278
417
|
input[type='checkbox'],
|
|
279
418
|
input[type='radio'] {
|
|
280
|
-
@apply w-
|
|
281
|
-
@apply focus:
|
|
419
|
+
@apply w-5 h-5 text-primary-600 bg-white border-gray-300 rounded-md transition-colors duration-200;
|
|
420
|
+
@apply focus:outline-2 focus:outline-offset-2 focus:outline-primary-500;
|
|
282
421
|
@apply disabled:bg-gray-100 disabled:cursor-not-allowed;
|
|
283
422
|
}
|
|
284
423
|
|
|
@@ -286,9 +425,16 @@
|
|
|
286
425
|
@apply rounded-full;
|
|
287
426
|
}
|
|
288
427
|
|
|
428
|
+
/* Ensure form controls use primary brand color. Checkbox/radio accent is
|
|
429
|
+
themeable independently of primary-500 via --panda-checkbox-accent
|
|
430
|
+
(falls back to primary-500, today's behaviour, when unset). */
|
|
431
|
+
input, select, textarea {
|
|
432
|
+
accent-color: var(--panda-checkbox-accent, var(--color-primary-500));
|
|
433
|
+
}
|
|
434
|
+
|
|
289
435
|
/* Label styling */
|
|
290
436
|
label {
|
|
291
|
-
@apply block text-
|
|
437
|
+
@apply block text-xs font-semibold text-gray-500 mb-1;
|
|
292
438
|
}
|
|
293
439
|
|
|
294
440
|
/* Field wrapper styling */
|
|
@@ -300,7 +446,7 @@
|
|
|
300
446
|
input.error,
|
|
301
447
|
textarea.error,
|
|
302
448
|
select.error {
|
|
303
|
-
@apply
|
|
449
|
+
@apply outline-error-500 focus:outline-error-500;
|
|
304
450
|
}
|
|
305
451
|
|
|
306
452
|
.field-error {
|
|
@@ -309,8 +455,8 @@
|
|
|
309
455
|
|
|
310
456
|
/* Button styling */
|
|
311
457
|
.btn {
|
|
312
|
-
@apply inline-flex items-center justify-center px-
|
|
313
|
-
@apply focus-visible:outline-2 focus-visible:outline-offset-2;
|
|
458
|
+
@apply inline-flex items-center justify-center px-4 py-2 text-sm font-medium rounded-xl transition-colors duration-200;
|
|
459
|
+
@apply focus-visible:outline-2 focus-visible:outline-offset-2 shadow-sm;
|
|
314
460
|
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
|
315
461
|
}
|
|
316
462
|
|
|
@@ -319,22 +465,22 @@
|
|
|
319
465
|
}
|
|
320
466
|
|
|
321
467
|
.btn-secondary {
|
|
322
|
-
@apply
|
|
468
|
+
@apply border border-gray-200 bg-white text-gray-700 hover:bg-gray-50 focus-visible:outline-gray-300;
|
|
323
469
|
}
|
|
324
470
|
|
|
325
471
|
.btn-danger {
|
|
326
|
-
@apply bg-error-
|
|
472
|
+
@apply border border-error-200 bg-error-50 text-error-600 hover:bg-error-100 focus-visible:outline-error-300;
|
|
327
473
|
}
|
|
328
474
|
|
|
329
475
|
.btn-success {
|
|
330
|
-
@apply bg-success-
|
|
476
|
+
@apply border border-success-200 bg-success-50 text-success-600 hover:bg-success-100 focus-visible:outline-success-300;
|
|
331
477
|
}
|
|
332
478
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
inset: 0;
|
|
479
|
+
/* Custom select dropdown */
|
|
480
|
+
[data-controller="custom-select"] {
|
|
481
|
+
position: relative;
|
|
337
482
|
}
|
|
483
|
+
|
|
338
484
|
}
|
|
339
485
|
|
|
340
486
|
/* EditorJS content styles */
|
|
@@ -457,3 +603,57 @@
|
|
|
457
603
|
}
|
|
458
604
|
}
|
|
459
605
|
}
|
|
606
|
+
|
|
607
|
+
/* Page tree indentation utilities - must be explicitly defined because
|
|
608
|
+
they're used in panda-cms views which aren't scanned by this build */
|
|
609
|
+
@layer utilities {
|
|
610
|
+
.ml-18 {
|
|
611
|
+
margin-left: var(--spacing-18, 4.5rem);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/* ButtonComponent action classes - generated dynamically in Ruby, so must be explicitly defined */
|
|
615
|
+
.bg-primary-500 {
|
|
616
|
+
background-color: var(--color-primary-500);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.bg-primary-600 {
|
|
620
|
+
background-color: var(--color-primary-600);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.hover\:bg-primary-600:hover {
|
|
624
|
+
background-color: var(--color-primary-600);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.focus-visible\:outline-primary-600:focus-visible {
|
|
628
|
+
outline-color: var(--color-primary-600);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* Sidebar navigation classes - used in dynamic Ruby string interpolation */
|
|
632
|
+
.space-y-0\.5 > :not(:last-child) {
|
|
633
|
+
margin-bottom: 0.125rem;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.pl-6 {
|
|
637
|
+
padding-left: 1.5rem;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.pl-8 {
|
|
641
|
+
padding-left: 2rem;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
.pr-2 {
|
|
645
|
+
padding-right: 0.5rem;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.text-white\/90 {
|
|
649
|
+
color: rgb(255 255 255 / 0.9);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.border-white\/20 {
|
|
653
|
+
border-color: rgb(255 255 255 / 0.2);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.hover\:bg-primary-500\/40:hover {
|
|
657
|
+
background-color: color-mix(in srgb, var(--color-primary-500) 40%, transparent);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
@@ -10,6 +10,12 @@ module Panda
|
|
|
10
10
|
super(attribute, text, options.reverse_merge(class: label_styles))
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
# Override field_id to match FormBuilder's signature, since the included
|
|
14
|
+
# FormTagHelper defines a field_id with a different arity that shadows it.
|
|
15
|
+
def field_id(method, *suffixes, namespace: @options[:namespace], index: @options[:index])
|
|
16
|
+
@template.field_id(@object_name, method, *suffixes, namespace: namespace, index: index)
|
|
17
|
+
end
|
|
18
|
+
|
|
13
19
|
def text_field(attribute, options = {})
|
|
14
20
|
# Extract custom label if provided
|
|
15
21
|
custom_label = options.delete(:label)
|
|
@@ -48,6 +54,22 @@ module Panda
|
|
|
48
54
|
end
|
|
49
55
|
end
|
|
50
56
|
|
|
57
|
+
def telephone_field(method, options = {})
|
|
58
|
+
custom_label = options.delete(:label)
|
|
59
|
+
|
|
60
|
+
content_tag :div, class: container_styles do
|
|
61
|
+
label(method, custom_label) + meta_text(options) + super(method, options.reverse_merge(class: input_styles)) + error_message(method)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def url_field(method, options = {})
|
|
66
|
+
custom_label = options.delete(:label)
|
|
67
|
+
|
|
68
|
+
content_tag :div, class: container_styles do
|
|
69
|
+
label(method, custom_label) + meta_text(options) + super(method, options.reverse_merge(class: input_styles)) + error_message(method)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
51
73
|
def datetime_field(method, options = {})
|
|
52
74
|
# Extract custom label if provided
|
|
53
75
|
custom_label = options.delete(:label)
|
|
@@ -57,6 +79,31 @@ module Panda
|
|
|
57
79
|
end
|
|
58
80
|
end
|
|
59
81
|
|
|
82
|
+
def datetime_split_field(method, options = {})
|
|
83
|
+
custom_label = options.delete(:label)
|
|
84
|
+
value = object.respond_to?(method) ? object.send(method) : nil
|
|
85
|
+
|
|
86
|
+
content_tag :div, class: container_styles, data: {controller: "datetime"} do
|
|
87
|
+
label(method, custom_label) +
|
|
88
|
+
meta_text(options) +
|
|
89
|
+
hidden_field(method, data: {datetime_target: "combinedField"}, value: value&.iso8601) +
|
|
90
|
+
content_tag(:div, class: "flex gap-3") do
|
|
91
|
+
@template.tag.input(
|
|
92
|
+
type: "date",
|
|
93
|
+
class: input_styles,
|
|
94
|
+
data: {datetime_target: "dateField", action: "change->datetime#update"}
|
|
95
|
+
) +
|
|
96
|
+
@template.tag.input(
|
|
97
|
+
type: "time",
|
|
98
|
+
step: "60",
|
|
99
|
+
class: input_styles,
|
|
100
|
+
data: {datetime_target: "timeField", action: "change->datetime#update"}
|
|
101
|
+
)
|
|
102
|
+
end +
|
|
103
|
+
error_message(method)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
60
107
|
def text_area(method, options = {})
|
|
61
108
|
# Extract custom label if provided
|
|
62
109
|
custom_label = options.delete(:label)
|
|
@@ -75,12 +122,24 @@ module Panda
|
|
|
75
122
|
end
|
|
76
123
|
end
|
|
77
124
|
|
|
78
|
-
def
|
|
125
|
+
def number_field(method, options = {})
|
|
79
126
|
# Extract custom label if provided
|
|
80
127
|
custom_label = options.delete(:label)
|
|
81
128
|
|
|
82
129
|
content_tag :div, class: container_styles do
|
|
83
|
-
label(method, custom_label) + meta_text(options) + super(method,
|
|
130
|
+
label(method, custom_label) + meta_text(options) + super(method, options.reverse_merge(class: input_styles)) + error_message(method)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def select(method, choices = nil, options = {}, html_options = {})
|
|
135
|
+
# Extract custom label if provided
|
|
136
|
+
custom_label = options.delete(:label)
|
|
137
|
+
|
|
138
|
+
html_options = html_options.reverse_merge(class: select_styles)
|
|
139
|
+
html_options[:data] = (html_options[:data] || {}).merge("custom-select-target": "select")
|
|
140
|
+
|
|
141
|
+
content_tag :div, class: container_styles, data: {controller: "custom-select"} do
|
|
142
|
+
label(method, custom_label) + meta_text(options) + super(method, choices, options, html_options) + select_svg + error_message(method)
|
|
84
143
|
end
|
|
85
144
|
end
|
|
86
145
|
|
|
@@ -88,19 +147,25 @@ module Panda
|
|
|
88
147
|
# Extract custom label if provided
|
|
89
148
|
custom_label = options.delete(:label)
|
|
90
149
|
|
|
91
|
-
|
|
92
|
-
|
|
150
|
+
html_options = html_options.reverse_merge(class: input_styles)
|
|
151
|
+
html_options[:data] = (html_options[:data] || {}).merge("custom-select-target": "select")
|
|
152
|
+
|
|
153
|
+
content_tag :div, class: container_styles, data: {controller: "custom-select"} do
|
|
154
|
+
label(method, custom_label) + meta_text(options) + super(method, collection, value_method, text_method, options, html_options) + error_message(method)
|
|
93
155
|
end
|
|
94
156
|
end
|
|
95
157
|
|
|
96
158
|
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
|
|
97
|
-
|
|
98
|
-
|
|
159
|
+
html_options = html_options.reverse_merge(class: select_styles)
|
|
160
|
+
html_options[:data] = (html_options[:data] || {}).merge("custom-select-target": "select")
|
|
161
|
+
|
|
162
|
+
content_tag :div, class: container_styles, data: {controller: "custom-select"} do
|
|
163
|
+
label(method) + meta_text(options) + super(
|
|
99
164
|
method,
|
|
100
165
|
priority_zones,
|
|
101
166
|
options,
|
|
102
|
-
html_options
|
|
103
|
-
)
|
|
167
|
+
html_options
|
|
168
|
+
) + error_message(method)
|
|
104
169
|
end
|
|
105
170
|
end
|
|
106
171
|
|
|
@@ -125,8 +190,7 @@ module Panda
|
|
|
125
190
|
content_tag :div, class: container_styles do
|
|
126
191
|
label(method, custom_label) +
|
|
127
192
|
meta_text(options) +
|
|
128
|
-
#
|
|
129
|
-
@template.content_tag(:link, nil, rel: "stylesheet", href: "https://cdn.jsdelivr.net/npm/cropperjs@1.6.2/dist/cropper.min.css") +
|
|
193
|
+
# Note: cropperjs 2.x uses Web Components with built-in styles — no external CSS needed
|
|
130
194
|
# File input
|
|
131
195
|
content_tag(:div, class: "mt-2") do
|
|
132
196
|
super(method, options.reverse_merge(
|
|
@@ -262,7 +326,7 @@ module Panda
|
|
|
262
326
|
value ||= submit_default_value
|
|
263
327
|
|
|
264
328
|
# Use the primary color for save/create actions
|
|
265
|
-
action = object
|
|
329
|
+
action = object&.persisted? ? :save : :create
|
|
266
330
|
button_classes = case action
|
|
267
331
|
when :save, :create
|
|
268
332
|
"text-white bg-primary-500 hover:bg-primary-600"
|
|
@@ -287,16 +351,48 @@ module Panda
|
|
|
287
351
|
custom_label = options.delete(:label)
|
|
288
352
|
|
|
289
353
|
content_tag :div, class: container_styles do
|
|
290
|
-
label(method, custom_label) + meta_text(options) + super(method, options.reverse_merge(class: "border-gray-300 ml-2"), checked_value, unchecked_value)
|
|
354
|
+
label(method, custom_label) + meta_text(options) + super(method, options.reverse_merge(class: "border-gray-300 text-primary-600 focus:ring-primary-500 ml-2"), checked_value, unchecked_value)
|
|
291
355
|
end
|
|
292
356
|
end
|
|
293
357
|
|
|
294
358
|
def date_field(method, options = {})
|
|
295
|
-
# Extract custom label if provided
|
|
296
359
|
custom_label = options.delete(:label)
|
|
360
|
+
date_min = options.delete(:min)
|
|
361
|
+
date_max = options.delete(:max)
|
|
297
362
|
|
|
298
|
-
|
|
299
|
-
|
|
363
|
+
# Format current value for display
|
|
364
|
+
raw_value = object.respond_to?(method) ? object.send(method) : nil
|
|
365
|
+
iso_value = if raw_value.respond_to?(:strftime)
|
|
366
|
+
raw_value.strftime("%Y-%m-%d")
|
|
367
|
+
else
|
|
368
|
+
raw_value
|
|
369
|
+
end
|
|
370
|
+
display_value = if raw_value.respond_to?(:strftime)
|
|
371
|
+
raw_value.strftime("%-d %b %Y")
|
|
372
|
+
elsif raw_value.is_a?(String) && raw_value.present?
|
|
373
|
+
raw_value
|
|
374
|
+
else
|
|
375
|
+
""
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
controller_data = {controller: "datepicker"}
|
|
379
|
+
controller_data[:datepicker_date_min_value] = date_min.to_s if date_min
|
|
380
|
+
controller_data[:datepicker_date_max_value] = date_max.to_s if date_max
|
|
381
|
+
|
|
382
|
+
content_tag :div, class: "#{container_styles} relative", data: controller_data do
|
|
383
|
+
label(method, custom_label) +
|
|
384
|
+
meta_text(options) +
|
|
385
|
+
hidden_field(method, value: iso_value, data: {datepicker_target: "hidden"}) +
|
|
386
|
+
@template.text_field_tag(
|
|
387
|
+
nil,
|
|
388
|
+
display_value,
|
|
389
|
+
class: "#{input_styles} cursor-pointer",
|
|
390
|
+
readonly: true,
|
|
391
|
+
placeholder: options[:placeholder] || "Select date...",
|
|
392
|
+
data: {datepicker_target: "display", action: "click->datepicker#toggle focus->datepicker#toggle"}
|
|
393
|
+
) +
|
|
394
|
+
content_tag(:div, "", class: "hidden absolute z-50 mt-1 left-0", data: {datepicker_target: "calendar"}) +
|
|
395
|
+
error_message(method)
|
|
300
396
|
end
|
|
301
397
|
end
|
|
302
398
|
|
|
@@ -332,8 +428,8 @@ module Panda
|
|
|
332
428
|
end
|
|
333
429
|
|
|
334
430
|
def section_heading(text, options = {})
|
|
335
|
-
@template.content_tag(:div, class: "-
|
|
336
|
-
@template.content_tag(:h3, text, class: "text-
|
|
431
|
+
@template.content_tag(:div, class: "mt-6 pt-4 border-t border-gray-200 mb-4") do
|
|
432
|
+
@template.content_tag(:h3, text, class: "text-sm font-semibold text-gray-700")
|
|
337
433
|
end
|
|
338
434
|
end
|
|
339
435
|
|
|
@@ -383,7 +479,13 @@ module Panda
|
|
|
383
479
|
end
|
|
384
480
|
|
|
385
481
|
def submit_default_value
|
|
386
|
-
object.
|
|
482
|
+
if object.nil?
|
|
483
|
+
"Submit"
|
|
484
|
+
elsif object.persisted?
|
|
485
|
+
"Update #{object.class.name.demodulize}"
|
|
486
|
+
else
|
|
487
|
+
"Create #{object.class.name.demodulize}"
|
|
488
|
+
end
|
|
387
489
|
end
|
|
388
490
|
|
|
389
491
|
def wrap_field(method, options = {}, &block)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%= content_tag :span, @attrs do %>
|
|
2
|
+
<%= text %>
|
|
3
|
+
<% if removable? %>
|
|
4
|
+
<%= content_tag :button, type: "button", class: "inline-flex items-center ml-1 hover:opacity-70", aria: { label: "Remove" } do %>
|
|
5
|
+
<%= content_tag :svg, class: "h-3 w-3", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor" do %>
|
|
6
|
+
<%= tag.path 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" %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% end %>
|