UNPKG

@lovebowls/leagueelements

Version:

League Elements package for LoveBowls

1,477 lines (1,328 loc) 1.29 MB
(function () { 'use strict'; const baseFontSizeConstants = (fontScale = 1.0) => ` /* Base font sizes for different contexts */ --le-font-size-base-desktop: ${16 * fontScale}px; --le-font-size-base-mobile: ${18 * fontScale}px; line-height: 1.2; `; // Shared font size base variables and UI element font sizes (used internally) const getFontSizeElementVariables = (isMobile = false) => ` /* Specific sizes for common UI elements */ --le-font-size-button: var(--le-font-size-base-${isMobile ? 'mobile' : 'desktop'}); --le-font-size-label: var(--le-font-size-base-${isMobile ? 'mobile' : 'desktop'}); --le-font-size-input: var(--le-font-size-base-${isMobile ? 'mobile' : 'desktop'}); --le-font-size-table-header: var(--le-font-size-base-${isMobile ? 'mobile' : 'desktop'}); --le-font-size-table-cell: 0.7em; --le-font-size-dropdown: var(--le-font-size-base-${isMobile ? 'mobile' : 'desktop'}); --le-padding-xs: 0.25rem; --le-padding-s: 0.6rem; --le-padding-m: 1rem; `; const getMobileStyles = (fontScale = 1.0) => ` :host { ${baseFontSizeConstants(fontScale)} font-size: var(--le-font-size-base-mobile); /* Mobile-specific styling that can be added to host elements */ --le-font-size-base: 1em; --le-font-size-xs: 0.6em; --le-font-size-small: 0.8em; --le-font-size-medium: 1.0em; --le-font-size-large: 1.2em; --le-font-size-xlarge: 1.4em; --le-font-size-xxlarge: 1.6em; ${getFontSizeElementVariables(true)} .no-data padding: var(--le-padding-m, 1rem); color: var(--le-text-color-secondary, #666); background-color: transparent; border: none; } .controls-panel { gap: var(--le-padding-s, 0.5rem); } /* Mobile-specific dropdown styling */ .controls-panel .filter-controls .dropdown-shared { flex: 1; } .controls-panel .dropdown-shared { flex: 1; } .controls-panel .dropdown-shared .dropdown-select-shared { width: 100%; padding: var(--le-padding-s, 0.75rem) calc(var(--le-padding-m, 1rem) * 2.5) var(--le-padding-s, 0.75rem) var(--le-padding-m, 1rem); min-height: 44px; /* Minimum touch target size */ background-size: 1.2rem; background-position: right 1rem center; border-width: 2px; } .controls-panel .dropdown-select-shared:focus { border-width: 2px; } /* Input fields on mobile */ .form-input-shared, .form-select-shared, { min-height: 44px; /* Standard mobile touch target */ padding: var(--le-padding-xs, 0.5rem) var(--le-padding-m, 1rem); font-size: var(--le-font-size-medium, 1.0em); border-width: 2px; width: 100% !important; /* Force width consistency on mobile */ max-width: 100%; box-sizing: border-box; } /* Specific handling for date inputs on mobile */ .form-input-shared[type="date"] { -webkit-appearance: none; -moz-appearance: textfield; appearance: none; } .form-input-shared:focus, .form-select-shared:focus { border-width: 2px; } .form-label-shared { margin-bottom: var(--le-padding-s, 0.75rem); } .resizer { display: none !important; } } `; const getDesktopStyles = (fontScale = 1.0) => ` :host { ${baseFontSizeConstants(fontScale)} font-size: var(--le-font-size-base-desktop); /* Standardized font sizes for desktop - these cascade to all sub-components */ --le-font-size-base: 1em; --le-font-size-xs: 0.75em; --le-font-size-small: 0.9em; --le-font-size-medium: 1.0em; --le-font-size-large: 1.2em; --le-font-size-xlarge: 1.4em; --le-font-size-xxlarge: 1.6em; ${getFontSizeElementVariables(false)} .no-data padding: var(--le-padding-m, 1rem); color: var(--le-text-color-secondary, #666); background-color: transparent; border: none; } /* Desktop-specific dropdown styling */ .controls-panel .filter-controls .dropdown-shared { width: auto; min-width: 200px; } .controls-panel .dropdown-shared .dropdown-select-shared { min-width: 160px; width: auto; } .resizer { width: 5px; min-width: 5px; cursor: col-resize; background-color: var(--swal-background-color-header); border-left: 1px solid var(--swal-border-color-light); border-right: 1px solid var(--swal-border-color-light); z-index: 10; } .resizer:hover { background: var(--le-text-color-secondary); } } `; // Shared base styles for dropdown elements const dropdownStyles = ` /* Shared base styles for dropdown select elements */ .dropdown-select-shared { appearance: none; -webkit-appearance: none; -moz-appearance: none; background-color: var(--le-background-color-panel, #fff); border: 1px solid var(--le-border-color-medium, #ddd); border-radius: var(--le-border-radius-small, 3px); padding: var(--le-padding-xs, 0.25rem); font-size: var(--le-font-size-base, 1em); color: var(--le-text-color-primary, #333); cursor: pointer; line-height: 1.4; max-width: 100%; width: auto; background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right 0.5em center; background-size: 1em; } .dropdown-select-shared:hover { border-color: var(--le-border-color-dark, #ccc); } .dropdown-select-shared:focus { outline: none; border-color: var(--le-text-color-accent, #2196f3); box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2); } .dropdown-shared { position: relative; display: inline-block; } /* Shared styles for dropdown menus (button-triggered) */ .list-item-actions { position: relative; /* Ensure actions container can contain absolutely positioned menu */ } .dropdown-menu { position: fixed; /* Use fixed positioning to escape container boundaries */ background-color: var(--le-background-color-panel, #fff); border: 1px solid var(--le-border-color-medium, #ddd); border-radius: var(--le-border-radius-standard, 4px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); min-width: 120px; z-index: 9999; /* Much higher z-index to appear above list containers */ margin-top: 2px; /* Small gap between button and menu */ } .dropdown-menu-item { padding: var(--le-padding-s, 0.5em) var(--le-padding-m, 0.75em); cursor: pointer; border-bottom: 1px solid var(--le-border-color-light, #eee); transition: background-color 0.2s ease; font-size: var(--le-font-size-base, 1em); color: var(--le-text-color-primary, #333); } .dropdown-menu-item:last-child { border-bottom: none; } .dropdown-menu-item:hover { background-color: var(--le-background-color-hover, #f5f5f5); } .dropdown-menu-item:first-child { border-top-left-radius: var(--le-border-radius-standard, 4px); border-top-right-radius: var(--le-border-radius-standard, 4px); } .dropdown-menu-item:last-child { border-bottom-left-radius: var(--le-border-radius-standard, 4px); border-bottom-right-radius: var(--le-border-radius-standard, 4px); } `; const panelStyles = ` .panel { margin-bottom: var(--le-padding-m, 1.25em); border: 1px solid var(--le-border-color-light, #f0f0f0); border-radius: var(--le-border-radius-standard, 4px); background-color: var(--le-background-color-panel, #fff); } .panel-header-shared { padding: var(--le-padding-s, 0.75em) var(--le-padding-m, 1.25em); /* Increased padding */ border-bottom: 1px solid var(--le-border-color-medium, #eee); font-weight: bold; color: var(--le-text-color-primary, #333); background-color: var(--le-background-color-header, #f9f9f9); display: flex; justify-content: space-between; align-items: center; font-size: var(--le-font-size-medium, 1.1em); } .panel .panel-header { display: flex; justify-content: space-between; align-items: center; background-color: var(--le-background-color-header, #f9f9f9); } .panel-content { padding: var(--le-padding-m, 1.25em); background-color: var(--le-background-color-panel, #fff); /* Common border for content area if needed border: 1px solid var(--le-border-color-light, #f0f0f0); */ } .controls-panel { /* Layout properties moved to mobile/desktop sections */ padding: 0; margin-bottom: var(--le-padding-s, 0.5rem); margin-top: var(--le-padding-s, 0.5rem); background: transparent; border: none; display: flex; justify-content: space-between; align-items: center; gap: var(--le-padding-m, 1rem); } .filter-controls { gap: var(--le-padding-s, 0.5rem); display: flex; align-items: center; flex: 1; } /* Enhanced dropdown styling for button-like appearance */ .controls-panel .dropdown-shared { position: relative; display: inline-block; } .controls-panel .dropdown-select-shared { appearance: none; -webkit-appearance: none; -moz-appearance: none; background-color: var(--le-background-color-button, #f0f0f0); border: 1px solid var(--le-border-color-medium, #ddd); border-radius: var(--le-border-radius-standard, 4px); padding: var(--le-padding-xs, 0.25rem); font-size: var(--le-font-size-dropdown, 1em)); color: var(--le-text-color-primary, #333); cursor: pointer; line-height: 1.4; min-width: 150px; transition: all 0.2s ease; font-weight: 500; } .controls-panel .dropdown-select-shared:hover { background-color: var(--le-background-color-button-hover, #e0e0e0); border-color: var(--le-border-color-dark, #ccc); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .controls-panel .dropdown-select-shared:focus { outline: none; border-color: var(--le-text-color-accent, #2196f3); box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2); background-color: var(--le-background-color-panel, #fff); } .controls-panel .dropdown-select-shared:active { background-color: var(--le-background-color-button-hover, #e0e0e0); transform: translateY(1px); } `; const buttonStyles = ` .button-shared { padding: 4px 8px; border: 1px solid var(--le-border-color-medium, #ccc); background-color: var(--le-background-color-button, #f0f0f0); color: var(--le-text-color-primary, #333); /* Ensure text color contrasts with button background */ cursor: pointer; border-radius: var(--le-border-radius-standard, 4px); font-size: var(--le-font-size-button, var(--le-font-size-medium, 1.15em)); /* Use variable with fallback */ text-decoration: none; display: inline-block; text-align: center; line-height: normal; /* Ensure consistent line height */ white-space: nowrap; /* Prevent text wrapping */ vertical-align: middle; /* Align nicely if next to text/icons */ user-select: none; /* Prevent text selection on click */ transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out; /* Smooth transitions */ } .button-shared:hover:not(:disabled) { background-color: var(--le-background-color-button-hover, #e0e0e0); border-color: var(--le-border-color-dark, #bbb); /* Slightly darker border on hover */ /* color: var(--le-text-color-accent-hover, inherit); Optional: change text color on hover */ } .button-shared:active:not(:disabled) { /* Optional: style for active (pressed) state */ /* background-color: var(--le-background-color-button-active, #d0d0d0); */ } .button-shared:disabled, .button-shared.disabled { /* Allow class-based disabling too */ background-color: var(--le-background-color-button-disabled, #eee); color: var(--le-text-color-secondary, #aaa); border-color: var(--le-border-color-medium, #ccc); /* Use medium border for disabled state */ cursor: not-allowed; opacity: 0.7; /* Visually indicate disabled state */ } /* Variations */ .button-shared.button-primary { background-color: var(--le-color-primary, #007bff); color: var(--le-text-color-on-primary, #fff); border-color: var(--le-color-primary, #007bff); } .button-shared.button-primary:hover:not(:disabled) { background-color: var(--le-color-primary-hover, #0056b3); border-color: var(--le-color-primary-hover, #0056b3); } .button-shared.button-secondary-light { background-color: var(--le-background-color-button-secondary-light, #f8f9fa); color: var(--le-text-color-secondary-light-text, #212529); border-color: var(--le-border-color-secondary-light, #ced4da); } .button-shared.button-secondary-light:hover:not(:disabled) { background-color: var(--le-background-color-button-secondary-light-hover, #e2e6ea); border-color: var(--le-border-color-secondary-light-hover, #dae0e5); color: var(--le-text-color-secondary-light-text-hover, #212529); } /* Example for a darker secondary button if needed elsewhere .button-shared.button-secondary { background-color: var(--le-color-secondary, #6c757d); color: var(--le-text-color-on-secondary, #fff); border-color: var(--le-color-secondary, #6c757d); } .button-shared.button-secondary:hover:not(:disabled) { background-color: var(--le-color-secondary-hover, #5a6268); border-color: var(--le-color-secondary-hover, #5a6268); } */ `; const modalStyles = ` .modal-shared-overlay { display: none; /* Hidden by default */ position: fixed; z-index: var(--le-z-index-modal-overlay, 1000); /* Ensure it's on top */ left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: var(--le-background-color-modal-overlay, rgba(0,0,0,0.4)); padding-top: 5%; padding-bottom: 5%; } .modal-shared-overlay.open { display: flex; justify-content: center; } .modal-shared-content { background-color: var(--le-background-color-panel, #fff); margin: 10% auto; /* Default to 10% from top, centered */ padding: 0; border: 1px solid var(--le-border-color-dark, #ccc); max-width: 600px; border-radius: var(--le-border-radius-large, 8px); box-shadow: var(--le-shadow-modal, 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)); display: flex; flex-direction: column; align-self: flex-start; } /* Specific styles for modal mobile-view class */ .modal-shared-content.mobile-view { width: 90%; /* Override any fixed width from shared styles */ margin: 0; /* Less margin from top on mobile */ max-width: 90%; min-width: 280px !important; } .modal-shared-header { padding: var(--le-padding-s, 0.75em) var(--le-padding-m, 1.25em); /* Increased padding */ border-bottom: 1px solid var(--le-border-color-medium, #eee); font-weight: bold; color: var(--le-text-color-primary, #333); background-color: var(--le-background-color-header, #f9f9f9); /* Modal header distinct background */ display: flex; justify-content: space-between; align-items: center; font-size: var(--le-font-size-large, 1.4em); /* Increased large font size */ border-top-left-radius: var(--le-border-radius-large, 8px); /* Match content radius */ border-top-right-radius: var(--le-border-radius-large, 8px); /* Match content radius */ } .modal-shared-header .close-button-shared { /* Specific styling for a close button if needed */ color: var(--le-text-color-secondary, #aaa); font-size: var(--le-font-size-xlarge, 1.8em); /* Increased size */ font-weight: bold; background: none; border: none; cursor: pointer; } .modal-shared-header .close-button-shared:hover, .modal-shared-header .close-button-shared:focus { color: var(--le-text-color-primary, #000); text-decoration: none; } .modal-shared-body { padding: var(--le-padding-m, 1.25em); /* Increased padding */ overflow-y: auto; /* Allow body to scroll if content is too long */ flex-grow: 1; /* Allows body to take up available space if modal has fixed height */ } .modal-shared-footer { padding: var(--le-padding-m, 1.25em); text-align: right; border-top: 1px solid var(--le-border-color-medium, #eee); background-color: var(--le-background-color-header, #f9f9f9); /* Optional: footer background */ border-bottom-left-radius: var(--le-border-radius-large, 8px); /* Match content radius */ border-bottom-right-radius: var(--le-border-radius-large, 8px); /* Match content radius */ } .modal-shared-footer .button-shared + .button-shared { /* Spacing between buttons in footer */ margin-left: var(--le-padding-s, 0.75em); /* Increased margin */ } `; const formStyles = ` .form-group-shared { margin-bottom: var(--le-padding-m, 1.25em); /* Increased margin */ } .form-label-shared { display: block; margin-bottom: var(--le-padding-xs, 0.4em); /* Increased margin */ font-weight: bold; color: var(--le-text-color-primary, #333); font-size: var(--le-font-size-label, 1em)); /* Use variable with fallback */ } .form-input-shared, .form-select-shared { width: 100%; height: auto; padding: var(--le-padding-s, 0.75em); border: 1px solid var(--le-border-color-dark, #ccc); border-radius: var(--le-border-radius-standard, 4px); box-sizing: border-box; font-size: var(--le-font-size-input, 1em)); /* Use variable with fallback */ color: var(--le-text-color-primary, #333); background-color: var(--le-background-color-panel, #fff); } /* Ensure date inputs have consistent styling across browsers */ .form-input-shared[type="date"] { max-width: 100%; } .form-input-shared:focus, .form-select-shared:focus { border-color: var(--le-border-color-accent, #2196f3); outline: none; /* Or a custom focus ring */ box-shadow: 0 0 0 2px var(--le-focus-ring-color, rgba(33, 150, 243, 0.3)); } /* Specific styling for checkbox groups if needed */ .form-checkbox-label-shared { display: flex; /* Changed to flex for better alignment */ align-items: center; font-weight: normal; /* Typically labels for checkboxes are not bold by default */ color: var(--le-text-color-primary, #333); } .form-checkbox-label-shared input[type="checkbox"] { margin-right: var(--le-padding-s, 0.75em); /* Increased margin */ /* Consider custom styling for checkboxes if desired, or rely on browser defaults */ /* For consistent appearance across browsers, custom checkbox styling can be complex */ /* For now, using default with adjusted margin */ width: auto; /* Override width: 100% from .form-input-shared if a generic class was applied */ vertical-align: middle; /* Align checkbox with text */ } /* Styling for a container of multiple checkboxes or radio buttons */ .form-options-group-shared { /* Styles for a group of checkboxes/radios, e.g., display: flex; flex-direction: column; gap: ... */ } /* Styling for individual option within a group */ .form-option-item-shared { /* Styles for each checkbox/radio item within a group */ } /* Enhanced checkbox styles for better mobile usability */ .checkbox-enhanced-shared { position: relative; display: inline-block; cursor: pointer; user-select: none; margin-right: var(--le-padding-s, 0.75em); } .checkbox-enhanced-shared input[type="checkbox"] { position: absolute; opacity: 0; cursor: pointer; height: 0; width: 0; } .checkbox-enhanced-shared .checkmark-shared { position: relative; display: inline-block; width: 1.5em; height: 1.5em; background-color: var(--le-background-color-panel, #fff); border: 2px solid var(--le-border-color-dark, #ccc); border-radius: var(--le-border-radius-small, 3px); transition: all 0.2s ease; vertical-align: middle; margin-right: var(--le-padding-xs, 0.25em); } /* Mobile-specific larger checkboxes */ @media (max-width: 768px) { .checkbox-enhanced-shared .checkmark-shared { width: 2em; height: 2em; border-width: 2px; } } /* Hover state */ .checkbox-enhanced-shared:hover input[type="checkbox"] ~ .checkmark-shared { border-color: var(--le-text-color-accent, #2196f3); background-color: var(--le-background-color-row-hover, #f9f9f9); } /* Focus state */ .checkbox-enhanced-shared input[type="checkbox"]:focus ~ .checkmark-shared { border-color: var(--le-text-color-accent, #2196f3); box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2); } /* Checked state */ .checkbox-enhanced-shared input[type="checkbox"]:checked ~ .checkmark-shared { background-color: var(--le-text-color-accent, #2196f3); border-color: var(--le-text-color-accent, #2196f3); } /* Checkmark icon */ .checkbox-enhanced-shared .checkmark-shared:after { content: ""; position: absolute; display: none; left: 50%; top: 50%; transform: translate(-50%, -50%) rotate(45deg); width: 0.4em; height: 0.8em; border: solid var(--le-text-color-on-primary, #fff); border-width: 0 0.15em 0.15em 0; } /* Mobile-specific larger checkmark */ @media (max-width: 768px) { .checkbox-enhanced-shared .checkmark-shared:after { width: 0.5em; height: 1em; border-width: 0 0.2em 0.2em 0; } } /* Show checkmark when checked */ .checkbox-enhanced-shared input[type="checkbox"]:checked ~ .checkmark-shared:after { display: block; } /* Disabled state */ .checkbox-enhanced-shared input[type="checkbox"]:disabled ~ .checkmark-shared { background-color: var(--le-background-color-button-disabled, #eee); border-color: var(--le-border-color-medium, #ddd); cursor: not-allowed; } .checkbox-enhanced-shared input[type="checkbox"]:disabled ~ .checkmark-shared:after { border-color: var(--le-text-color-secondary, #aaa); } /* Legend/checkbox list styles for responsive layouts */ .checkbox-list-responsive-shared { display: grid; gap: var(--le-padding-s, 0.75em); grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); } /* Mobile-specific responsive checkbox list */ @media (max-width: 768px) { .checkbox-list-responsive-shared { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--le-padding-m, 1em); } } /* Extra small screens - force 2 columns */ @media (max-width: 480px) { .checkbox-list-responsive-shared { grid-template-columns: 1fr 1fr; gap: var(--le-padding-s, 0.75em); } } /* Legend item styling for checkbox lists */ .legend-item-shared { display: flex; align-items: center; font-size: var(--le-font-size-small, 0.9em); padding: var(--le-padding-xs, 0.25em); border-radius: var(--le-border-radius-small, 3px); transition: background-color 0.2s ease; } .legend-item-shared:hover { background-color: var(--le-background-color-row-hover, #f9f9f9); } /* Mobile-specific legend item styling */ @media (max-width: 768px) { .legend-item-shared { font-size: var(--le-font-size-medium, 1.2em); padding: var(--le-padding-s, 0.75em) var(--le-padding-xs, 0.25em); min-height: 3em; } } `; const listStyles = ` .list-container { max-height: 200px; overflow-y: auto; } .list-panel { border: 1px solid var(--le-border-color-medium, #ddd); border-radius: var(--le-border-radius-standard, 4px); background: var(--le-background-color-panel, #fff); } .list-panel h4 { margin: 0; padding: var(--le-padding-m, 1rem); background: var(--le-background-color-header, #f8f9fa); border-bottom: 1px solid var(--le-border-color-light, #eee); color: var(--le-text-color-primary, #333); } .list-panel.disabled { pointer-events: none; opacity: 0.6; } .list { list-style: none; margin: 0; padding: 0; } .list-item{ display: flex; justify-content: space-between; align-items: center; padding: var(--le-padding-xs) var(--le-padding-s); border-bottom: 1px solid var(--le-border-color-light, #eee); cursor: pointer; transition: background-color 0.2s ease; min-height: 2.5rem; /* Ensure consistent height for action buttons */ box-sizing: border-box; } .list-panel.disabled .list-item { cursor: not-allowed; } .list-item:last-child { border-bottom: none; } /* Example of a text part within a list item that should grow */ .list-item .list-item-text-primary { flex-grow: 1; font-size: var(--le-font-size-medium, 1.2em); text-align: left; /* Ensure text is left-aligned */ display: flex; align-items: center; /* Center text vertically within its container */ } /* Example of an actions container part within a list item */ .list-item .list-item-actions { flex-shrink: 0; /* Prevent actions from shrinking */ display: flex; gap: var(--le-padding-xs, 0.4em); /* Increased gap */ align-items: center; min-height: 2rem; /* Reserve space for buttons even when empty */ } .list-item:hover { background-color: var(--le-background-color-hover, #f5f5f5); } .list-item.selected { background-color: var(--le-background-color-selected, #e3f2fd); border-left: 3px solid var(--le-border-color-primary, #2196f3); animation: highlightItem 0.6s ease-out; } @keyframes highlightItem { 0% { background-color: var(--le-background-color-accent, #108CFF); box-shadow: 0 0 0 2px rgba(16, 140, 255, 0.3), 0 2px 8px rgba(16, 140, 255, 0.2); } 100% { background-color: var(--le-background-color-selected, #e3f2fd); box-shadow: none; } } /* Mobile-specific list item adjustments */ @media (max-width: 768px) { .list-item { min-height: 4rem; /* Slightly taller for mobile touch targets */ padding: 0 var(--le-padding-xs, 1rem);; } .list-item .list-item-actions { min-height: 2.5rem; /* Larger touch targets for mobile */ } .list-item .list-item-actions .button-shared { min-height: 2.5rem; /* Ensure buttons are touch-friendly */ } } `; // SweetAlert2 Mobile-Specific Style Overrides // These styles are specifically designed to improve the appearance and usability // of SweetAlert2 dialogs on smaller screens (mobile devices). // They should be injected globally by elements that use SweetAlert2. const sweetAlertMobileOverrides = ` /* Injected SweetAlert2 Mobile Styles - Production v1 */ .swal2-popup.swal-popup-mobile { width: 90vw !important; max-width: 480px !important; padding: var(--le-padding-m, 1rem) !important; /* Increased padding for better touch spacing */ font-size: var(--le-font-size-base, 1.5em) !important; /* Use mobile base font size */ } .swal-popup-mobile .swal2-title, .swal-title-mobile { font-size: var(--le-font-size-large, 1.4em) !important; /* Readable title size for mobile */ padding: 0.5rem 0.5rem 0.75rem !important; /* Adjusted padding */ margin-bottom: 0 !important; /* Remove default bottom margin if any, handled by container */ line-height: 1.3 !important; } .swal-popup-mobile .swal2-html-container, .swal-html-container-mobile { font-size: var(--le-font-size-small, 1.0em) !important; /* Use shared variable instead of hardcoded rem */ margin: 0.75rem 0.5rem !important; /* Vertical and horizontal margin */ line-height: 1.5 !important; text-align: center !important; } .swal-popup-mobile .swal2-actions, .swal-actions-mobile { width: 100% !important; margin-top: 1rem !important; gap: 0.65rem !important; /* Space between stacked buttons */ flex-direction: column-reverse !important; /* Stack buttons, confirm on top */ } .swal-popup-mobile .swal2-styled, .swal-styled-mobile { width: 100% !important; /* Full width buttons */ padding: 0.85rem !important; /* Generous padding for touch targets */ font-size: var(--le-font-size-medium, 1.2em) !important; margin: 0 !important; /* Remove individual margins, gap handles spacing */ border-radius: 0.3rem !important; } /* Optional: If you want to ensure default button colors are explicitly set or overridden for mobile */ /* .swal-popup-mobile .swal2-confirm.swal-styled-mobile { background-color: #3085d6 !important; /* Example: Default confirm blue */ /* } .swal-popup-mobile .swal2-cancel.swal-styled-mobile { background-color: #d33 !important; /* Example: Default cancel red */ /* } */ `; // SweetAlert2 Global Style Overrides // These are general global styles for SweetAlert2 dialogs, ensuring a consistent // base appearance. They should be injected globally by elements that use SweetAlert2. const sweetAlertGlobalStyles = ` /* SweetAlert2 Global Styles */ .swal2-popup { font-family: var(--le-font-family-base, sans-serif); /* Assuming you have a base font family variable */ background-color: var(--le-background-color-panel, #fff) !important; border-radius: var(--le-border-radius-large, 8px) !important; border: 1px solid var(--le-border-color-dark, #ccc) !important; color: var(--le-text-color-primary, #333) !important; } .swal2-title { color: var(--le-text-color-primary, #333) !important; font-size: var(--le-font-size-large, 1.4em) !important; /* Match modal header */ font-weight: bold !important; } .swal2-html-container { color: var(--le-text-color-primary, #333) !important; font-size: var(--le-font-size-base, 1em) !important; margin: var(--le-padding-m, 1.25em) !important; /* Add some margin */ } .swal2-actions { margin-top: var(--le-padding-m, 1.25em) !important; } .swal2-styled { /* Base for swal buttons */ padding: var(--le-padding-s, 0.75em) var(--le-padding-m, 1.25em) !important; border-radius: var(--le-border-radius-standard, 4px) !important; font-size: var(--le-font-size-medium, 1.15em) !important; text-decoration: none !important; /* display: inline-block !important; */ /* Removed to allow SweetAlert2 to control visibility */ text-align: center !important; line-height: normal !important; white-space: nowrap !important; vertical-align: middle !important; user-select: none !important; transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out !important; border: 1px solid transparent !important; /* Start with transparent border, colors will override */ } .swal2-styled:focus { outline: none !important; box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.2) !important; /* Example focus, adjust as needed */ } .swal2-styled:hover:not(:disabled) { /* General hover, specific button types might override */ filter: brightness(90%); } .swal2-confirm { background-color: var(--le-color-confirm, #28a745) !important; /* Define --le-color-confirm or use existing */ color: var(--le-text-color-on-primary, #fff) !important; border-color: var(--le-color-confirm, #28a745) !important; } .swal2-confirm:hover:not(:disabled) { background-color: var(--le-color-confirm-hover, #218838) !important; /* Define --le-color-confirm-hover */ border-color: var(--le-color-confirm-hover, #218838) !important; } .swal2-cancel { background-color: var(--le-color-cancel, #dc3545) !important; /* Define --le-color-cancel or use existing */ color: var(--le-text-color-on-primary, #fff) !important; border-color: var(--le-color-cancel, #dc3545) !important; } .swal2-cancel:hover:not(:disabled) { background-color: var(--le-color-cancel-hover, #c82333) !important; /* Define --le-color-cancel-hover */ border-color: var(--le-color-cancel-hover, #c82333) !important; } /* If you use a deny button */ .swal2-deny { background-color: var(--le-color-deny, #6c757d) !important; /* Define --le-color-deny */ color: var(--le-text-color-on-primary, #fff) !important; border-color: var(--le-color-deny, #6c757d) !important; } .swal2-deny:hover:not(:disabled) { background-color: var(--le-color-deny-hover, #5a6268) !important; /* Define --le-color-deny-hover */ border-color: var(--le-color-deny-hover, #5a6268) !important; } /* Icon styling - optional, if you want to color default icons */ .swal2-icon.swal2-warning { color: var(--le-text-color-warning, #ffc107) !important; /* Define --le-text-color-warning */ border-color: var(--le-text-color-warning, #ffc107) !important; } .swal2-icon.swal2-error { color: var(--le-text-color-error, #dc3545) !important; /* Define --le-text-color-error */ border-color: var(--le-text-color-error, #dc3545) !important; } .swal2-icon.swal2-success { color: var(--le-text-color-success, #28a745) !important; /* Define --le-text-color-success */ border-color: var(--le-text-color-success, #28a745) !important; } .swal2-icon.swal2-info { color: var(--le-text-color-info, #17a2b8) !important; /* Define --le-text-color-info */ border-color: var(--le-text-color-info, #17a2b8) !important; } `; const tabStyles = ` /* Tab System Styles */ .modal-tabs-container { display: flex; flex-direction: column; height: 100%; } .tab-navigation { display: flex; border-bottom: 2px solid var(--le-border-color-light); margin-bottom: var(--le-padding-m); gap: 0; } .tab-button { background: var(--le-background-color-button); border: 1px solid var(--le-border-color-medium); border-bottom: none; padding: var(--le-padding-s) var(--le-padding-m); cursor: pointer; font-weight: 500; color: var(--le-text-color-secondary); border-radius: var(--le-border-radius-standard) var(--le-border-radius-standard) 0 0; position: relative; transition: all 0.2s ease; min-width: 120px; text-align: center; user-select: none; } .tab-button:hover { background: var(--le-background-color-button-hover); color: var(--le-text-color-primary); } .tab-button.active { background: var(--le-background-color-panel); color: var(--le-text-color-primary); font-weight: 600; border-bottom: 2px solid var(--le-background-color-panel); margin-bottom: -2px; z-index: 1; } .tab-button:not(:last-child) { border-right: none; } .tab-content-container { flex: 1; min-height: 300px; } .tab-content { display: none; animation: fadeIn 0.2s ease-in; } .tab-content.active { display: block; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* Enhanced fieldset styling for tabs */ .tab-content fieldset { margin-bottom: var(--le-padding-m); border: 1px solid var(--le-border-color-medium); border-radius: var(--le-border-radius-standard); padding: var(--le-padding-m); background: var(--le-background-color-header); } .tab-content fieldset legend { font-weight: 600; color: var(--le-text-color-primary); padding: 0 var(--le-padding-s); font-size: var(--le-font-size-small, 0.8em); } .tab-content fieldset:last-child { margin-bottom: 0; } /* Enhanced form styling within tabs */ .tab-content .form-group { margin-bottom: var(--le-padding-m); } .tab-content .form-group:last-child { margin-bottom: 0; } .tab-content .form-group label { display: block; margin-bottom: var(--le-padding-xs); font-weight: 500; color: var(--le-text-color-primary); font-size: var(--le-font-size-small, 0.8em); } .tab-content .form-group input[type="text"], .tab-content .form-group input[type="number"], .tab-content .form-group select { width: 100%; padding: var(--le-padding-s); border: 1px solid var(--le-border-color-dark); border-radius: var(--le-border-radius-standard); box-sizing: border-box; font-size: var(--le-font-size-medium); transition: border-color 0.2s ease, box-shadow 0.2s ease; } .tab-content .form-group input[type="text"]:focus, .tab-content .form-group input[type="number"]:focus, .tab-content .form-group select:focus { outline: none; border-color: var(--le-text-color-accent); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.1); } .tab-content .form-group input[type="checkbox"] { margin-right: var(--le-padding-s); transform: scale(1.1); } /* Enhanced rink points settings */ .tab-content .rink-points-settings { border: 1px dashed var(--le-border-color-dark); padding: var(--le-padding-m); margin-top: var(--le-padding-s); background-color: var(--le-background-color-header); border-radius: var(--le-border-radius-standard); transition: opacity 0.3s ease; } .tab-content .rink-points-settings.disabled { opacity: 0.6; } /* Responsive adjustments for tabs */ @media (max-width: 600px) { .tab-button { padding: var(--le-padding-xs) var(--le-padding-s); min-width: 100px; font-size: var(--le-font-size-small); } .form-group-grid { grid-template-columns: 1fr; gap: var(--le-padding-s); } .tab-content fieldset { padding: var(--le-padding-s); } } `; const helpBannerStyles = ` /* Help Banner Styles */ .help-banner-shared { background-color: var(--le-background-color-header, #f8f9fa); padding: var(--le-padding-xs, 0.25rem) var(--le-padding-m, 1rem); text-align: right; font-size: var(--le-font-size-xs, 0.75em); border-bottom: 1px solid var(--le-border-color-medium, #eee); display: flex; justify-content: flex-end; align-items: center; gap: var(--le-padding-s, 0.5rem); } .help-banner-shared a { color: var(--le-text-color-accent, #2196f3); text-decoration: none; font-weight: 500; } .help-banner-shared a:hover { text-decoration: underline; } .help-banner-shared .separator { color: var(--le-text-color-secondary, #666); margin: 0 var(--le-padding-xs, 0.25rem); } `; const pagingStyles = ` /* Paging Controls Styles */ .paging-controls { display: flex; justify-content: flex-end; gap: var(--le-padding-s, 0.5rem); margin-top: var(--le-padding-s, 0.5rem); padding: var(--le-padding-xs, 0.25rem) 0; background-color: transparent; border: none; font-size: var(--le-font-size-xs, 0.75em); } .paging-btn { background: var(--le-background-color-button, #f5f5f5); border: 1px solid var(--le-border-color-dark, #ccc); border-radius: var(--le-border-radius-small, 3px); padding: var(--le-padding-xs, 0.2rem) var(--le-padding-s, 0.7rem); cursor: pointer; color: var(--le-text-color-primary, #333); transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out; user-select: none; } .paging-btn:hover:not(:disabled) { background: var(--le-background-color-button-hover, #e0e0e0); border-color: var(--le-border-color-dark, #bbb); } .paging-btn:disabled { background: var(--le-background-color-button-disabled, #eee); color: var(--le-text-color-secondary, #aaa); cursor: not-allowed; opacity: 0.7; } `; const BASE_STYLES$8 = ` ${panelStyles} ${buttonStyles} ${listStyles} ${pagingStyles} :host { display: block; font-family: var(--le-font-family-main, 'Open Sans', Helvetica, Arial, sans-serif); box-sizing: border-box; color: var(--le-text-color-primary, #333); } .match-item { padding: var(--swal-padding-xs, 0.2rem) 0; display: flex; align-items: center; gap: var(--le-padding-xs, 0.25em); } .match-date { color: var(--le-text-color-secondary, #666); margin-bottom: var(--le-padding-xs, 0.2em); } .error { color: var(--le-text-color-error, #ff0000); padding: var(--le-padding-s, 0.5rem); background-color: var(--le-background-color-error, #fff0f0); border-radius: var(--swal-border-radius-standard, 4px); } .warning-icon-future-result { color: var(--le-color-status-warning, #f39c12); } .warning-icon-conflict { color: var(--le-color-status-conflict, #e67e22); } .warning-icon-pending-result { color: var(--le-color-status-pending, #e74c3c); } .warning-icon-no-date { color: var(--le-color-status-info, #2196f3); } .warning-icon { flex-shrink: 0; } `; const MOBILE_STYLES$5 = ` ${BASE_STYLES$8} .match-item { padding: var(--swal-padding-xs, 0.2rem) 0; } /* Mobile: Ensure no height constraints for dynamic content-based height */ .attention-matches { max-height: none; overflow-y: visible; } `; const DESKTOP_STYLES$5 = ` ${BASE_STYLES$8} .match-item { padding: var(--swal-padding-xs, 0.2rem) 0; } /* Desktop: Add height constraints if needed for space management */ .attention-matches { max-height: 300px; overflow-y: auto; } `; const TEMPLATE$2 = ` <div class="attention-matches"> {{attentionMatches}} </div> <div class="paging-controls" id="attention-paging" {{showPaging}}> <button class="paging-btn button-shared button" id="attention-prev" {{prevDisabled}}>&lt; Prev</button> <button class="paging-btn button-shared button" id="attention-next" {{nextDisabled}}>Next &gt;</button> </div> `; /** * Safely register a custom element, avoiding duplicate registration errors * in single-page applications where modules may be loaded multiple times. * * @param {string} tagName - The custom element tag name (e.g., 'league-element') * @param {CustomElementConstructor} elementClass - The element class constructor * @param {boolean} [logRegistration=false] - Whether to log successful registrations */ function safeDefine(tagName, elementClass, logRegistration = false) { if (!customElements.get(tagName)) { customElements.define(tagName, elementClass); if (logRegistration) { console.log(`[ElementRegistry] Registered custom element: ${tagName}`); } } else if (logRegistration) { console.log(`[ElementRegistry] Custom element already registered: ${tagName}`); } } // Define custom event types for the LeagueMatchesAttention element class LeagueMatchesAttentionEvent extends CustomEvent { constructor(detail) { super('league-matches-attention-event', { detail, bubbles: true, // Ensure event bubbles up through the DOM composed: true, // Ensure event crosses shadow DOM boundaries cancelable: true // Make the event cancelable }); } } /** * Custom element to display matches requiring attention with paging. * * @element league-matches-attention * @attr {string} data - JSON stringified League instance object * @attr {boolean} [is-mobile] - Whether to use mobile styles * @attr {string} [team-mapping] - JSON stringified array of {value, label} objects mapping team values to display names * * Emits 'league-matches-attention-event' with detail { type: 'matchClick', match } */ class LeagueMatchesAttention extends HTMLElement { constructor() { super(); this.shadow = this.attachShadow({ mode: 'open' }); this.league = null; // Store the league instance instead of just matches this.currentPage = 0; this.itemsPerPage = 5; this.teamMapping = {}; } static get observedAttributes() { return ['data', 'is-mobile', 'team-mapping']; } connectedCallback() { if (this.hasAttribute('team-mapping')) { this._setTeamMapping(this.getAttribute('team-mapping')); } this.render(); } attributeChangedCallback(name, oldValue, newValue) { if (oldValue === newValue) return; if (name === 'data') { this.loadData(newValue); } else if (name === 'is-mobile') { this.render(); } else if (name === 'team-mapping') { this._setTeamMapping(newValue); this.render(); // Re-render to apply new team names } } _setTeamMapping(mappingData) { try { if (!mappingData) { this.teamMapping = {}; return; } // Parse mapping from the attribute string const dataObj = typeof mappingData === 'string' ? JSON.parse(mappingData) : mappingData; // Handle either array of {_id, name} objects or direct _id -> name mapping object if (Array.isArray(dataObj)) { // Convert array of objects to a simple map this.teamMapping = {}; dataObj.forEach(team => { if (team._id) { this.teamMapping[team._id] = team.name || team._id; } }); } else { // Assume we already have a direct mapping object this.teamMapping = dataObj; } } catch (error) { console.error('[LeagueMatchesAttention] Error parsing team mapping:', error); this.teamMapping = {}; } } // Add a utility method to get display name for a team getTeamDisplayName(teamId) { if (!teamId || !this.teamMapping) { return teamId; } // With standardized team model, teamMapping is a simple object map of _id -> name const teamName = this.teamMapping[teamId]; if (teamName) { return teamName; } return teamId; } /** * Loads and parses the league data. * @param {string|Object} data - League instance or JSON string */ async loadData(data) { try { if (typeof data === 'string') { const parsedData = JSON.parse(data); // Import League class to create a proper instance const { League } = await Promise.resolve().then(function () { return index; }); this.league = new League(parsedData); } else if (data && typeof data === 'object') { // If it's already a League instance, use it directly if (data.getMatchesRequiringAttention && data.getConflictingMatchIds) { this.league = data; } else { // It's plain data, create a League instance const { League } = await Promise.resolve().then(function () { return index; }); this.league = new League(data); } } else { this.league = null; } this.currentPage = 0; this.render(); this.dispatchEvent(new LeagueMatchesAttentionEvent({ type: 'dataLoaded', league: this.league })); } catch (error) { const errorMessage = 'Failed to load league data for attention matches'; this.showError(errorMessage); console.error('Error loading league data for attention matches:', error); this.dispatchEvent(new LeagueMatchesAttentionEvent({ type: 'error', message: errorMessage, error })); } } /** * Shows an error message in the component. * @param {string} message */ showError(message) { const content = this.shadow.querySelector('.attention-matches'); if (content) { content.innerHTML = `<div class="error">${message}</div>`; } } _hasNextPage() { if (!this.league || typeof this.league.getMatchesRequiringAttention !== 'function') { return false; } const list = this.league.getMatchesRequiringAttention(); return (this.currentPage + 1) * this.itemsPerPage < list.length; } /** * Renders the list of matches requiring attention for the current page. * @returns {string} */ renderAttentionMatches() { if (!this.league || typeof this.league.getMatchesRequiringAttention !== 'function') { return '<div class="no-data">No league data available</div>'; } const matches = this.league.getMatchesRequiringAttention(); const start = this.currentPage * this.itemsPerPage; const pageItems = matches.slice(start, start + this.itemsPerPage); if (pageItems.length === 0) { if (matches.length > 0) { return '<div class="no-data">None</div>'; } return '<div class="no-data">None</div>'; } const today = new Date(); today.setHours(0, 0, 0, 0); let conflictingIds = new Set(); if (typeof this.league.getConflictingMatchIds === 'function') { conflictingIds = this.league.getConflictingMatchIds(); } return pageItems.map(match => { // Get display names for teams const homeTeamId = match.homeTeam?._id; const awayTeamId = match.awayTeam?._id; const homeTeamDisplay = match.homeTeam?.name || this.getTeamDisplayName(homeTeamId); const awayTeamDisplay = match.awayTeam?.name || this.getTeamDisplayName(awayTeamId); let warningSymbol = ''; let warningClass = ''; let tooltipText = ''; if (match.result && match.date) { const matchDate = new Date(match.date); matchDate.setHours(0, 0, 0, 0); if (matchDate > today) { tooltipText = "Result entered for a future match date."; warningSymbol = '&#9888;'; warningClass = 'warning-icon-future-result'; } } else if (conflictingIds.has(match._id)) { tooltipText = "Scheduling conflict on this date."; warningSy