Compare commits
6 Commits
ae134907a4
...
996ddad2ea
| Author | SHA1 | Date | |
|---|---|---|---|
| 996ddad2ea | |||
| f486e02413 | |||
| 69a0989b7a | |||
| 0c4682e4d7 | |||
| bcac8e5adc | |||
| e1b47e5b62 |
@@ -45,7 +45,7 @@ from .services import (
|
|||||||
SESSION_USER_KEY = "uid"
|
SESSION_USER_KEY = "uid"
|
||||||
ACCOUNT_SESSION_KEY = "account_sid"
|
ACCOUNT_SESSION_KEY = "account_sid"
|
||||||
|
|
||||||
ALLOWED_CLIENTS = {"blog", "market", "cart", "events", "federation", "artdag", "artdag_l2"}
|
ALLOWED_CLIENTS = {"blog", "market", "cart", "events", "federation", "orders", "artdag", "artdag_l2"}
|
||||||
|
|
||||||
|
|
||||||
def register(url_prefix="/auth"):
|
def register(url_prefix="/auth"):
|
||||||
|
|||||||
34
blog/templates/_types/post/admin/_nav_entries_oob.html
Normal file
34
blog/templates/_types/post/admin/_nav_entries_oob.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{# OOB swap for nav entries and calendars — blog's version using shared macro #}
|
||||||
|
{% from 'macros/nav_entries.html' import nav_entries_oob %}
|
||||||
|
|
||||||
|
{% set has_items = (associated_entries and associated_entries.entries) or calendars %}
|
||||||
|
{% call nav_entries_oob(has_items) %}
|
||||||
|
{% if associated_entries and associated_entries.entries %}
|
||||||
|
{% for entry in associated_entries.entries %}
|
||||||
|
{% set _entry_path = '/' + post.slug + '/calendars/' + entry.calendar_slug + '/' + entry.start_at.year|string + '/' + entry.start_at.month|string + '/' + entry.start_at.day|string + '/entries/' + entry.id|string + '/' %}
|
||||||
|
<a
|
||||||
|
href="{{ events_url(_entry_path) }}"
|
||||||
|
class="{{styles.nav_button_less_pad}}">
|
||||||
|
<div class="w-8 h-8 rounded bg-stone-200 flex-shrink-0"></div>
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<div class="font-medium truncate">{{ entry.name }}</div>
|
||||||
|
<div class="text-xs text-stone-600 truncate">
|
||||||
|
{{ entry.start_at.strftime('%b %d, %Y at %H:%M') }}
|
||||||
|
{% if entry.end_at %} – {{ entry.end_at.strftime('%H:%M') }}{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if calendars %}
|
||||||
|
{% for calendar in calendars %}
|
||||||
|
{% set local_href=events_url('/' + post.slug + '/calendars/' + calendar.slug + '/') %}
|
||||||
|
<a
|
||||||
|
href="{{ local_href }}"
|
||||||
|
class="{{styles.nav_button_less_pad}}">
|
||||||
|
<i class="fa fa-calendar" aria-hidden="true"></i>
|
||||||
|
<div>{{calendar.name}}</div>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endcall %}
|
||||||
@@ -32,7 +32,8 @@ def register():
|
|||||||
g.s, user_id=user_id, session_id=session_id,
|
g.s, user_id=user_id, session_id=session_id,
|
||||||
)
|
)
|
||||||
count = summary.count + summary.calendar_count + summary.ticket_count
|
count = summary.count + summary.calendar_count + summary.ticket_count
|
||||||
return await render_template("fragments/cart_mini.html", cart_count=count)
|
oob = request.args.get("oob", "")
|
||||||
|
return await render_template("fragments/cart_mini.html", cart_count=count, oob=oob)
|
||||||
|
|
||||||
async def _account_nav_item():
|
async def _account_nav_item():
|
||||||
from shared.infrastructure.urls import cart_url
|
from shared.infrastructure.urls import cart_url
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div id="cart-mini">
|
<div id="cart-mini" {% if oob %}hx-swap-oob="true"{% endif %}>
|
||||||
{% if cart_count == 0 %}
|
{% if cart_count == 0 %}
|
||||||
<div class="h-12 w-12 rounded-full overflow-hidden border border-stone-300 flex-shrink-0">
|
<div class="h-12 w-12 rounded-full overflow-hidden border border-stone-300 flex-shrink-0">
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ Routes:
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from quart import Blueprint, g, request, render_template, render_template_string, make_response
|
from quart import Blueprint, g, request, render_template, make_response
|
||||||
|
|
||||||
from shared.browser.app.utils.htmx import is_htmx_request
|
from shared.browser.app.utils.htmx import is_htmx_request
|
||||||
from shared.infrastructure.cart_identity import current_cart_identity
|
from shared.infrastructure.cart_identity import current_cart_identity
|
||||||
from shared.infrastructure.data_client import fetch_data
|
from shared.infrastructure.data_client import fetch_data
|
||||||
from shared.contracts.dtos import CartSummaryDTO, PostDTO, dto_from_dict
|
from shared.contracts.dtos import PostDTO, dto_from_dict
|
||||||
from shared.services.registry import services
|
from shared.services.registry import services
|
||||||
|
|
||||||
|
|
||||||
@@ -125,28 +125,24 @@ def register() -> Blueprint:
|
|||||||
# Load entry DTO for the widget template
|
# Load entry DTO for the widget template
|
||||||
entry = await services.calendar.entry_by_id(g.s, entry_id)
|
entry = await services.calendar.entry_by_id(g.s, entry_id)
|
||||||
|
|
||||||
# Updated cart count for OOB mini-cart
|
# Commit so cross-service calls see the updated tickets
|
||||||
summary_params = {}
|
await g.tx.commit()
|
||||||
if ident["user_id"] is not None:
|
g.tx = await g.s.begin()
|
||||||
summary_params["user_id"] = ident["user_id"]
|
|
||||||
if ident["session_id"] is not None:
|
from shared.infrastructure.fragments import fetch_fragment
|
||||||
summary_params["session_id"] = ident["session_id"]
|
frag_params = {"oob": "1"}
|
||||||
raw_summary = await fetch_data("cart", "cart-summary", params=summary_params, required=False)
|
if ident["user_id"] is not None:
|
||||||
summary = dto_from_dict(CartSummaryDTO, raw_summary) if raw_summary else CartSummaryDTO()
|
frag_params["user_id"] = str(ident["user_id"])
|
||||||
cart_count = summary.count + summary.calendar_count + summary.ticket_count
|
if ident["session_id"] is not None:
|
||||||
|
frag_params["session_id"] = ident["session_id"]
|
||||||
|
|
||||||
# Render widget + OOB cart-mini
|
|
||||||
widget_html = await render_template(
|
widget_html = await render_template(
|
||||||
"_types/page_summary/_ticket_widget.html",
|
"_types/page_summary/_ticket_widget.html",
|
||||||
entry=entry,
|
entry=entry,
|
||||||
qty=qty,
|
qty=qty,
|
||||||
ticket_url="/all-tickets/adjust",
|
ticket_url="/all-tickets/adjust",
|
||||||
)
|
)
|
||||||
mini_html = await render_template_string(
|
mini_html = await fetch_fragment("cart", "cart-mini", params=frag_params, required=False)
|
||||||
'{% from "_types/cart/_mini.html" import mini with context %}'
|
|
||||||
'{{ mini(oob="true") }}',
|
|
||||||
cart_count=cart_count,
|
|
||||||
)
|
|
||||||
return await make_response(widget_html + mini_html, 200)
|
return await make_response(widget_html + mini_html, 200)
|
||||||
|
|
||||||
return bp
|
return bp
|
||||||
|
|||||||
@@ -3,14 +3,11 @@ from datetime import datetime, timezone
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from quart import (
|
from quart import (
|
||||||
request, render_template, render_template_string, make_response,
|
request, render_template, make_response,
|
||||||
Blueprint, g, redirect, url_for, jsonify,
|
Blueprint, g, redirect, url_for, jsonify,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
from sqlalchemy import update, func as sa_func
|
|
||||||
|
|
||||||
from models.calendars import CalendarEntry
|
|
||||||
|
|
||||||
|
|
||||||
from .services.entries import (
|
from .services.entries import (
|
||||||
@@ -206,40 +203,21 @@ def register():
|
|||||||
entry.ticket_price = ticket_price
|
entry.ticket_price = ticket_price
|
||||||
entry.ticket_count = ticket_count
|
entry.ticket_count = ticket_count
|
||||||
|
|
||||||
# Count pending calendar entries from local session (sees the just-added entry)
|
# Commit so cross-service calls see the new entry
|
||||||
user_id = getattr(g, "user", None) and g.user.id
|
await g.tx.commit()
|
||||||
cal_filters = [
|
g.tx = await g.s.begin()
|
||||||
CalendarEntry.deleted_at.is_(None),
|
|
||||||
CalendarEntry.state == "pending",
|
|
||||||
]
|
|
||||||
if user_id:
|
|
||||||
cal_filters.append(CalendarEntry.user_id == user_id)
|
|
||||||
|
|
||||||
cal_count = await g.s.scalar(
|
|
||||||
select(sa_func.count()).select_from(CalendarEntry).where(*cal_filters)
|
|
||||||
) or 0
|
|
||||||
|
|
||||||
# Get product cart count via HTTP
|
|
||||||
from shared.infrastructure.cart_identity import current_cart_identity
|
from shared.infrastructure.cart_identity import current_cart_identity
|
||||||
from shared.infrastructure.data_client import fetch_data
|
from shared.infrastructure.fragments import fetch_fragment
|
||||||
from shared.contracts.dtos import CartSummaryDTO, dto_from_dict
|
|
||||||
ident = current_cart_identity()
|
ident = current_cart_identity()
|
||||||
summary_params = {}
|
frag_params = {"oob": "1"}
|
||||||
if ident["user_id"] is not None:
|
if ident["user_id"] is not None:
|
||||||
summary_params["user_id"] = ident["user_id"]
|
frag_params["user_id"] = str(ident["user_id"])
|
||||||
if ident["session_id"] is not None:
|
if ident["session_id"] is not None:
|
||||||
summary_params["session_id"] = ident["session_id"]
|
frag_params["session_id"] = ident["session_id"]
|
||||||
raw_summary = await fetch_data("cart", "cart-summary", params=summary_params, required=False)
|
|
||||||
cart_summary = dto_from_dict(CartSummaryDTO, raw_summary) if raw_summary else CartSummaryDTO()
|
|
||||||
product_count = cart_summary.count
|
|
||||||
total_count = product_count + cal_count
|
|
||||||
|
|
||||||
html = await render_template("_types/day/_main_panel.html")
|
html = await render_template("_types/day/_main_panel.html")
|
||||||
mini_html = await render_template_string(
|
mini_html = await fetch_fragment("cart", "cart-mini", params=frag_params, required=False)
|
||||||
'{% from "_types/cart/_mini.html" import mini with context %}'
|
|
||||||
'{{ mini(oob="true") }}',
|
|
||||||
cart_count=total_count,
|
|
||||||
)
|
|
||||||
return await make_response(html + mini_html, 200)
|
return await make_response(html + mini_html, 200)
|
||||||
|
|
||||||
@bp.get("/add/")
|
@bp.get("/add/")
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ def register():
|
|||||||
@bp.before_request
|
@bp.before_request
|
||||||
async def load_entry():
|
async def load_entry():
|
||||||
"""Load the calendar entry from the URL parameter."""
|
"""Load the calendar entry from the URL parameter."""
|
||||||
|
from quart import abort
|
||||||
entry_id = request.view_args.get("entry_id")
|
entry_id = request.view_args.get("entry_id")
|
||||||
if entry_id:
|
if entry_id:
|
||||||
result = await g.s.execute(
|
result = await g.s.execute(
|
||||||
@@ -60,6 +61,8 @@ def register():
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
g.entry = result.scalar_one_or_none()
|
g.entry = result.scalar_one_or_none()
|
||||||
|
if g.entry is None:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
@bp.context_processor
|
@bp.context_processor
|
||||||
async def inject_entry():
|
async def inject_entry():
|
||||||
|
|||||||
@@ -8,12 +8,10 @@ Routes:
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from quart import Blueprint, g, request, render_template, render_template_string, make_response
|
from quart import Blueprint, g, request, render_template, make_response
|
||||||
|
|
||||||
from shared.browser.app.utils.htmx import is_htmx_request
|
from shared.browser.app.utils.htmx import is_htmx_request
|
||||||
from shared.infrastructure.cart_identity import current_cart_identity
|
from shared.infrastructure.cart_identity import current_cart_identity
|
||||||
from shared.infrastructure.data_client import fetch_data
|
|
||||||
from shared.contracts.dtos import CartSummaryDTO, dto_from_dict
|
|
||||||
from shared.services.registry import services
|
from shared.services.registry import services
|
||||||
|
|
||||||
|
|
||||||
@@ -108,28 +106,24 @@ def register() -> Blueprint:
|
|||||||
# Load entry DTO for the widget template
|
# Load entry DTO for the widget template
|
||||||
entry = await services.calendar.entry_by_id(g.s, entry_id)
|
entry = await services.calendar.entry_by_id(g.s, entry_id)
|
||||||
|
|
||||||
# Updated cart count for OOB mini-cart
|
# Commit so cross-service calls see the updated tickets
|
||||||
summary_params = {}
|
await g.tx.commit()
|
||||||
if ident["user_id"] is not None:
|
g.tx = await g.s.begin()
|
||||||
summary_params["user_id"] = ident["user_id"]
|
|
||||||
if ident["session_id"] is not None:
|
from shared.infrastructure.fragments import fetch_fragment
|
||||||
summary_params["session_id"] = ident["session_id"]
|
frag_params = {"oob": "1"}
|
||||||
raw_summary = await fetch_data("cart", "cart-summary", params=summary_params, required=False)
|
if ident["user_id"] is not None:
|
||||||
summary = dto_from_dict(CartSummaryDTO, raw_summary) if raw_summary else CartSummaryDTO()
|
frag_params["user_id"] = str(ident["user_id"])
|
||||||
cart_count = summary.count + summary.calendar_count + summary.ticket_count
|
if ident["session_id"] is not None:
|
||||||
|
frag_params["session_id"] = ident["session_id"]
|
||||||
|
|
||||||
# Render widget + OOB cart-mini
|
|
||||||
widget_html = await render_template(
|
widget_html = await render_template(
|
||||||
"_types/page_summary/_ticket_widget.html",
|
"_types/page_summary/_ticket_widget.html",
|
||||||
entry=entry,
|
entry=entry,
|
||||||
qty=qty,
|
qty=qty,
|
||||||
ticket_url=f"/{g.post_slug}/tickets/adjust",
|
ticket_url=f"/{g.post_slug}/tickets/adjust",
|
||||||
)
|
)
|
||||||
mini_html = await render_template_string(
|
mini_html = await fetch_fragment("cart", "cart-mini", params=frag_params, required=False)
|
||||||
'{% from "_types/cart/_mini.html" import mini with context %}'
|
|
||||||
'{{ mini(oob="true") }}',
|
|
||||||
cart_count=cart_count,
|
|
||||||
)
|
|
||||||
return await make_response(widget_html + mini_html, 200)
|
return await make_response(widget_html + mini_html, 200)
|
||||||
|
|
||||||
return bp
|
return bp
|
||||||
|
|||||||
@@ -286,6 +286,10 @@ def register() -> Blueprint:
|
|||||||
ticket_type_id=tt.id,
|
ticket_type_id=tt.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Commit so cart's callback to events sees the updated tickets
|
||||||
|
await g.tx.commit()
|
||||||
|
g.tx = await g.s.begin()
|
||||||
|
|
||||||
# Compute cart count for OOB mini-cart update
|
# Compute cart count for OOB mini-cart update
|
||||||
from shared.infrastructure.data_client import fetch_data
|
from shared.infrastructure.data_client import fetch_data
|
||||||
from shared.contracts.dtos import CartSummaryDTO, dto_from_dict
|
from shared.contracts.dtos import CartSummaryDTO, dto_from_dict
|
||||||
|
|||||||
34
events/templates/_types/post/admin/_nav_entries_oob.html
Normal file
34
events/templates/_types/post/admin/_nav_entries_oob.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{# OOB swap for nav entries and calendars when toggling associations or editing calendars #}
|
||||||
|
{% from 'macros/nav_entries.html' import nav_entries_oob %}
|
||||||
|
|
||||||
|
{% set has_items = (associated_entries and associated_entries.entries) or calendars %}
|
||||||
|
{% call nav_entries_oob(has_items) %}
|
||||||
|
{% if associated_entries and associated_entries.entries %}
|
||||||
|
{% for entry in associated_entries.entries %}
|
||||||
|
{% set _entry_path = '/' + post.slug + '/calendars/' + entry.calendar_slug + '/' + entry.start_at.year|string + '/' + entry.start_at.month|string + '/' + entry.start_at.day|string + '/entries/' + entry.id|string + '/' %}
|
||||||
|
<a
|
||||||
|
href="{{ events_url(_entry_path) }}"
|
||||||
|
class="{{styles.nav_button_less_pad}}">
|
||||||
|
<div class="w-8 h-8 rounded bg-stone-200 flex-shrink-0"></div>
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<div class="font-medium truncate">{{ entry.name }}</div>
|
||||||
|
<div class="text-xs text-stone-600 truncate">
|
||||||
|
{{ entry.start_at.strftime('%b %d, %Y at %H:%M') }}
|
||||||
|
{% if entry.end_at %} – {{ entry.end_at.strftime('%H:%M') }}{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if calendars %}
|
||||||
|
{% for calendar in calendars %}
|
||||||
|
{% set local_href=events_url('/' + post.slug + '/calendars/' + calendar.slug + '/') %}
|
||||||
|
<a
|
||||||
|
href="{{ local_href }}"
|
||||||
|
class="{{styles.nav_button_less_pad}}">
|
||||||
|
<i class="fa fa-calendar" aria-hidden="true"></i>
|
||||||
|
<div>{{calendar.name}}</div>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endcall %}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
{# OOB swap for nav entries and calendars when toggling associations or editing calendars #}
|
|
||||||
{% import 'macros/links.html' as links %}
|
|
||||||
|
|
||||||
{# Associated Entries and Calendars - vertical on mobile, horizontal with arrows on desktop #}
|
|
||||||
{% if (associated_entries and associated_entries.entries) or calendars %}
|
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center gap-2 border-r border-stone-200 mr-2 sm:max-w-2xl"
|
|
||||||
id="entries-calendars-nav-wrapper"
|
|
||||||
hx-swap-oob="true">
|
|
||||||
{# Left scroll arrow - desktop only #}
|
|
||||||
<button
|
|
||||||
class="entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
|
|
||||||
aria-label="Scroll left"
|
|
||||||
_="on click
|
|
||||||
set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft - 200">
|
|
||||||
<i class="fa fa-chevron-left"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div id="associated-items-container"
|
|
||||||
class="overflow-y-auto sm:overflow-x-auto sm:overflow-y-visible scrollbar-hide max-h-[50vh] sm:max-h-none"
|
|
||||||
style="scroll-behavior: smooth;"
|
|
||||||
_="on load or scroll
|
|
||||||
if window.innerWidth >= 640 and my.scrollWidth > my.clientWidth
|
|
||||||
remove .hidden from .entries-nav-arrow
|
|
||||||
add .flex to .entries-nav-arrow
|
|
||||||
else
|
|
||||||
add .hidden to .entries-nav-arrow
|
|
||||||
remove .flex from .entries-nav-arrow
|
|
||||||
end">
|
|
||||||
<div class="flex flex-col sm:flex-row gap-1">
|
|
||||||
{# Calendar entries #}
|
|
||||||
{% if associated_entries and associated_entries.entries %}
|
|
||||||
{% for entry in associated_entries.entries %}
|
|
||||||
{% set _entry_path = '/' + post.slug + '/calendars/' + entry.calendar_slug + '/' + entry.start_at.year|string + '/' + entry.start_at.month|string + '/' + entry.start_at.day|string + '/entries/' + entry.id|string + '/' %}
|
|
||||||
<a
|
|
||||||
href="{{ events_url(_entry_path) }}"
|
|
||||||
class="{{styles.nav_button_less_pad}}">
|
|
||||||
<div class="w-8 h-8 rounded bg-stone-200 flex-shrink-0"></div>
|
|
||||||
<div class="flex-1 min-w-0">
|
|
||||||
<div class="font-medium truncate">{{ entry.name }}</div>
|
|
||||||
<div class="text-xs text-stone-600 truncate">
|
|
||||||
{{ entry.start_at.strftime('%b %d, %Y at %H:%M') }}
|
|
||||||
{% if entry.end_at %} – {{ entry.end_at.strftime('%H:%M') }}{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{# Calendar links #}
|
|
||||||
{% if calendars %}
|
|
||||||
{% for calendar in calendars %}
|
|
||||||
{% set local_href=events_url('/' + post.slug + '/calendars/' + calendar.slug + '/') %}
|
|
||||||
<a
|
|
||||||
href="{{ local_href }}"
|
|
||||||
class="{{styles.nav_button_less_pad}}">
|
|
||||||
<i class="fa fa-calendar" aria-hidden="true"></i>
|
|
||||||
<div>{{calendar.name}}</div>
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.scrollbar-hide::-webkit-scrollbar { display: none; }
|
|
||||||
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
{# Right scroll arrow - desktop only #}
|
|
||||||
<button
|
|
||||||
class="entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
|
|
||||||
aria-label="Scroll right"
|
|
||||||
_="on click
|
|
||||||
set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft + 200">
|
|
||||||
<i class="fa fa-chevron-right"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
{# Empty placeholder to remove nav items when all are disassociated/deleted #}
|
|
||||||
<div id="entries-calendars-nav-wrapper" hx-swap-oob="true"></div>
|
|
||||||
{% endif %}
|
|
||||||
58
shared/browser/templates/macros/nav_entries.html
Normal file
58
shared/browser/templates/macros/nav_entries.html
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{#
|
||||||
|
Composable scrollable nav entries container.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
{% call nav_entries_oob(has_items=True) %}
|
||||||
|
<a href="..." class="{{styles.nav_button_less_pad}}">...</a>
|
||||||
|
<a href="..." class="{{styles.nav_button_less_pad}}">...</a>
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
|
Each domain (events, blog, market) provides its own items via the caller block.
|
||||||
|
#}
|
||||||
|
|
||||||
|
{% macro nav_entries_oob(has_items) %}
|
||||||
|
{% if has_items %}
|
||||||
|
<div class="flex flex-col sm:flex-row sm:items-center gap-2 border-r border-stone-200 mr-2 sm:max-w-2xl"
|
||||||
|
id="entries-calendars-nav-wrapper"
|
||||||
|
hx-swap-oob="true">
|
||||||
|
<button
|
||||||
|
class="entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
|
||||||
|
aria-label="Scroll left"
|
||||||
|
_="on click
|
||||||
|
set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft - 200">
|
||||||
|
<i class="fa fa-chevron-left"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div id="associated-items-container"
|
||||||
|
class="overflow-y-auto sm:overflow-x-auto sm:overflow-y-visible scrollbar-hide max-h-[50vh] sm:max-h-none"
|
||||||
|
style="scroll-behavior: smooth;"
|
||||||
|
_="on load or scroll
|
||||||
|
if window.innerWidth >= 640 and my.scrollWidth > my.clientWidth
|
||||||
|
remove .hidden from .entries-nav-arrow
|
||||||
|
add .flex to .entries-nav-arrow
|
||||||
|
else
|
||||||
|
add .hidden to .entries-nav-arrow
|
||||||
|
remove .flex from .entries-nav-arrow
|
||||||
|
end">
|
||||||
|
<div class="flex flex-col sm:flex-row gap-1">
|
||||||
|
{{ caller() }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.scrollbar-hide::-webkit-scrollbar { display: none; }
|
||||||
|
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="entries-nav-arrow hidden flex-shrink-0 p-2 hover:bg-stone-200 rounded"
|
||||||
|
aria-label="Scroll right"
|
||||||
|
_="on click
|
||||||
|
set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft + 200">
|
||||||
|
<i class="fa fa-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div id="entries-calendars-nav-wrapper" hx-swap-oob="true"></div>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
Reference in New Issue
Block a user