tooltips now fully styled, appearing above applet container to avoid clipping issues; new methods added to apps.lyric.models.Token
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
function initGameKitTooltips() {
|
||||
const portal = document.getElementById('id_tooltip_portal');
|
||||
if (!portal) return;
|
||||
|
||||
document.querySelectorAll('#id_game_kit .token').forEach(token => {
|
||||
const tooltip = token.querySelector('.token-tooltip');
|
||||
if (!tooltip) return;
|
||||
|
||||
token.addEventListener('mouseenter', () => {
|
||||
const rect = token.getBoundingClientRect();
|
||||
portal.innerHTML = tooltip.innerHTML;
|
||||
portal.style.left = Math.round(rect.left + rect.width / 2) + 'px';
|
||||
portal.style.top = Math.round(rect.top) + 'px';
|
||||
portal.style.transform = 'translate(-50%, calc(-100% - 0.5rem))';
|
||||
portal.classList.add('active');
|
||||
});
|
||||
|
||||
token.addEventListener('mouseleave', () => {
|
||||
portal.classList.remove('active');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initGameKitTooltips);
|
||||
|
||||
@@ -65,20 +65,33 @@ class Token(models.Model):
|
||||
token_type = models.CharField(max_length=8, choices=TOKEN_TYPE_CHOICES)
|
||||
expires_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
def tooltip_text(self):
|
||||
if self.token_type == self.COIN:
|
||||
return (
|
||||
"Coin-on-a-String: Admit 1 Entry"
|
||||
" (and another after that, and another after that\u2026)"
|
||||
" \u2014 no expiry"
|
||||
)
|
||||
if self.token_type == self.FREE:
|
||||
return (
|
||||
f"Free Token: Admit 1 Entry"
|
||||
f" \u2014 Expires {self.expires_at.strftime('%Y-%m-%d')}"
|
||||
)
|
||||
def tooltip_name(self):
|
||||
return self.get_token_type_display()
|
||||
|
||||
def tooltip_description(self):
|
||||
if self.token_type in (self.COIN, self.FREE):
|
||||
return "Admit 1 Entry"
|
||||
return ""
|
||||
|
||||
def tooltip_expiry(self):
|
||||
if self.token_type == self.COIN:
|
||||
return "no expiry"
|
||||
if self.expires_at:
|
||||
return f"Expires {self.expires_at.strftime('%Y-%m-%d')}"
|
||||
return ""
|
||||
|
||||
def tooltip_shoptalk(self):
|
||||
if self.token_type == self.COIN:
|
||||
return "\u2026and another after that, and another after that\u2026"
|
||||
return None
|
||||
|
||||
def tooltip_text(self):
|
||||
text = f"{self.tooltip_name()}: {self.tooltip_description()}"
|
||||
if self.tooltip_shoptalk():
|
||||
text += f" ({self.tooltip_shoptalk()})"
|
||||
text += f" \u2014 {self.tooltip_expiry()}"
|
||||
return text
|
||||
|
||||
class PaymentMethod(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="payment_methods")
|
||||
stripe_pm_id = models.CharField(max_length=255)
|
||||
|
||||
@@ -61,14 +61,10 @@ class GameboardNavigationTest(FunctionalTest):
|
||||
ActionChains(self.browser).move_to_element(coin).perform()
|
||||
self.wait_for(
|
||||
lambda: self.assertTrue(
|
||||
self.browser.find_element(
|
||||
By.CSS_SELECTOR, "#id_kit_coin_on_a_string .token-tooltip"
|
||||
).is_displayed()
|
||||
self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
|
||||
)
|
||||
)
|
||||
coin_tooltip = self.browser.find_element(
|
||||
By.CSS_SELECTOR, "#id_kit_coin_on_a_string .token-tooltip"
|
||||
).text
|
||||
coin_tooltip = self.browser.find_element(By.ID, "id_tooltip_portal").text
|
||||
self.assertIn("Coin-on-a-String", coin_tooltip)
|
||||
self.assertIn("Admit 1 Entry", coin_tooltip)
|
||||
self.assertIn("and another after that", coin_tooltip)
|
||||
@@ -78,14 +74,10 @@ class GameboardNavigationTest(FunctionalTest):
|
||||
ActionChains(self.browser).move_to_element(free_token).perform()
|
||||
self.wait_for(
|
||||
lambda: self.assertTrue(
|
||||
self.browser.find_element(
|
||||
By.CSS_SELECTOR, "#id_kit_free_token_0 .token-tooltip"
|
||||
).is_displayed()
|
||||
self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
|
||||
)
|
||||
)
|
||||
free_tooltip = self.browser.find_element(
|
||||
By.CSS_SELECTOR, "#id_kit_free_token_0 .token-tooltip"
|
||||
).text
|
||||
free_tooltip = self.browser.find_element(By.ID, "id_tooltip_portal").text
|
||||
self.assertIn("Free Token", free_tooltip)
|
||||
self.assertIn("Admit 1 Entry", free_tooltip)
|
||||
self.assertIn("Expires", free_tooltip)
|
||||
|
||||
@@ -41,35 +41,29 @@ body.page-gameboard {
|
||||
}
|
||||
}
|
||||
|
||||
#id_game_applets_container {
|
||||
#id_applet_game_kit {
|
||||
#id_applet_game_kit {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
#id_game_kit {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: visible;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
overflow-x: visible;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar { display: none; }
|
||||
|
||||
#id_game_kit {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
overflow-x: visible;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar { display: none; }
|
||||
.token { position: static; }
|
||||
|
||||
.token-tooltip {
|
||||
z-index: 1000;
|
||||
}
|
||||
.token:hover .token-tooltip { display: none; }
|
||||
|
||||
.token,
|
||||
.kit-item {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.token,
|
||||
.kit-item { font-size: 1.5rem; }
|
||||
|
||||
.kit-item {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
.kit-item { opacity: 0.6; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +88,13 @@ body.page-gameboard {
|
||||
}
|
||||
}
|
||||
|
||||
#id_tooltip_portal {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
|
||||
&.active { display: block; }
|
||||
}
|
||||
|
||||
@media (max-height: 500px) {
|
||||
body.page-gameboard {
|
||||
.container {
|
||||
|
||||
@@ -1,3 +1,38 @@
|
||||
.token-tooltip {
|
||||
display: none;
|
||||
width: 16rem;
|
||||
max-width: 16rem;
|
||||
white-space: normal;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(6px);
|
||||
border: 0.1rem solid rgba(var(--secUser), 0.5);
|
||||
color: rgba(var(--secUser), 1);
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
z-index: 10;
|
||||
font-size: 0.875rem;
|
||||
|
||||
h4 {
|
||||
font-size: 0.95rem;
|
||||
margin: 0 0 0.3rem 0;
|
||||
color: rgba(var(--terUser), 1);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 0.2rem 0;
|
||||
|
||||
&.expiry {
|
||||
color: rgba(var(--priRd), 1);
|
||||
}
|
||||
}
|
||||
|
||||
small {
|
||||
display: block;
|
||||
font-size: 0.6rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.token {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
@@ -5,23 +40,13 @@
|
||||
color: rgba(var(--terUser), 1);
|
||||
|
||||
.token-tooltip {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 125%;
|
||||
left: 0;
|
||||
width: 16rem;
|
||||
max-width: 16rem;
|
||||
white-space: normal;
|
||||
background-color: rgba(var(--priUser), 0.95);
|
||||
border: 0.1rem solid rgba(var(--secUser), 0.5);
|
||||
color: rgba(var(--secUser), 1);
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
z-index: 10;
|
||||
font-size: 0.875rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
&:hover .token-tooltip {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,31 @@
|
||||
{% if coin %}
|
||||
<div id="id_kit_coin_on_a_string" class="token">
|
||||
<i class="fa-solid fa-clover"></i>
|
||||
<span class="token-tooltip">{{ coin.tooltip_text }}</span>
|
||||
<div class="token-tooltip">
|
||||
<h4>{{ coin.tooltip_name }}</h4>
|
||||
<p>
|
||||
{{ coin.tooltip_description }}
|
||||
</p>
|
||||
{% if coin.tooltip_shoptalk %}
|
||||
<small><em>{{ coin.tooltip_shoptalk }}</em></small>
|
||||
{% endif %}
|
||||
<p class="expiry">{{ coin.tooltip_expiry }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for token in free_tokens %}
|
||||
<div id="id_kit_free_token_{{ forloop.counter0 }}" class="token">
|
||||
<i class="fa-solid fa-coins"></i>
|
||||
<span class="token-tooltip">{{ token.tooltip_text }}</span>
|
||||
<div class="token-tooltip">
|
||||
<h4>{{ token.tooltip_name }}</h4>
|
||||
<p>
|
||||
{{ token.tooltip_description }}
|
||||
</p>
|
||||
{% if token.tooltip_shoptalk %}
|
||||
<small><em>{{ token.tooltip_shoptalk }}</em></small>
|
||||
{% endif %}
|
||||
<p class="expiry">{{ token.tooltip_expiry }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div id="id_kit_card_deck" class="kit-item"><i class="fa-regular fa-id-badge"></i></div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends "core/base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title_text %}Gameboard{% endblock title_text %}
|
||||
{% block header_text %}Gameboard{% endblock header_text %}
|
||||
@@ -8,4 +9,9 @@
|
||||
{% include "apps/applets/_partials/_gear.html" with menu_id="id_game_applet_menu" %}
|
||||
{% include "apps/gameboard/_partials/_applets.html" %}
|
||||
</div>
|
||||
{% endblock content %}
|
||||
<div id="id_tooltip_portal" class="token-tooltip"></div>
|
||||
{% endblock content %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{% static 'apps/gameboard/gameboard.js' %}"></script>
|
||||
{% endblock scripts %}
|
||||
|
||||
Reference in New Issue
Block a user