diff --git a/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py b/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py new file mode 100644 index 0000000..9314ad7 --- /dev/null +++ b/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py @@ -0,0 +1,39 @@ +# Generated by Django 5.2.7 on 2026-01-09 05:52 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0002_socialmediasettings_alter_navigationsettings_options_and_more'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.AlterField( + model_name='socialmediasettings', + name='links', + field=wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('facebook', 'Facebook'), ('twitter', 'Twitter'), ('instagram', 'Instagram'), ('threads', 'Threads'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})}), + ), + migrations.CreateModel( + name='BannerSnippet', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(blank=True, help_text='識別用 key(例如 home / category)', max_length=50)), + ('title', models.CharField(blank=True, max_length=255)), + ('link_url', models.URLField(blank=True)), + ('link_text', models.CharField(blank=True, max_length=100)), + ('is_active', models.BooleanField(default=True)), + ('sort_order', models.PositiveIntegerField(default=0)), + ('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'verbose_name': 'Banner', + 'verbose_name_plural': 'Banners', + 'ordering': ['sort_order', 'id'], + }, + ), + ] diff --git a/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py b/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py new file mode 100644 index 0000000..d64738e --- /dev/null +++ b/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.2.7 on 2026-01-09 09:01 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0003_alter_socialmediasettings_links_bannersnippet'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.RemoveField( + model_name='headersettings', + name='logo', + ), + migrations.AddField( + model_name='headersettings', + name='logo_dark', + field=models.ForeignKey(blank=True, help_text='淺色底用(深色 logo)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + migrations.AddField( + model_name='headersettings', + name='logo_light', + field=models.ForeignKey(blank=True, help_text='深色底用(亮色 logo)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + ] diff --git a/innovedus_cms/base/models.py b/innovedus_cms/base/models.py index 99355ee..0824424 100644 --- a/innovedus_cms/base/models.py +++ b/innovedus_cms/base/models.py @@ -29,12 +29,21 @@ from wagtail import blocks @register_setting class HeaderSettings(BaseGenericSetting): - logo = models.ForeignKey( + logo_light = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", + help_text="深色底用(亮色 logo)", + ) + logo_dark = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="淺色底用(深色 logo)", ) site_name = models.CharField(max_length=255, blank=True) extra_links = StreamField([ @@ -47,7 +56,8 @@ class HeaderSettings(BaseGenericSetting): panels = [ MultiFieldPanel( [ - FieldPanel("logo"), + FieldPanel("logo_light"), + FieldPanel("logo_dark"), FieldPanel("site_name"), FieldPanel("extra_links"), ], @@ -82,7 +92,7 @@ class SocialLinkBlock(blocks.StructBlock): ("facebook", "Facebook"), ("twitter", "Twitter"), ("instagram", "Instagram"), - ("thread", "Thread"), + ("threads", "Threads"), ("linkedin", "LinkedIn"), ("youtube", "YouTube"), ] @@ -102,6 +112,40 @@ class SocialMediaSettings(BaseGenericSetting): panels = [FieldPanel("links")] +@register_snippet +class BannerSnippet(models.Model): + key = models.CharField(max_length=50, blank=True, help_text="識別用 key(例如 home / category)") + title = models.CharField(max_length=255, blank=True) + image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=False, + on_delete=models.SET_NULL, + related_name="+", + ) + link_url = models.URLField(blank=True) + link_text = models.CharField(max_length=100, blank=True) + is_active = models.BooleanField(default=True) + sort_order = models.PositiveIntegerField(default=0) + + panels = [ + FieldPanel("key"), + FieldPanel("title"), + FieldPanel("image"), + FieldPanel("link_url"), + FieldPanel("link_text"), + FieldPanel("is_active"), + FieldPanel("sort_order"), + ] + + class Meta: + ordering = ["sort_order", "id"] + verbose_name = "Banner" + verbose_name_plural = "Banners" + + def __str__(self): + return self.title or f"Banner {self.pk}" + @register_snippet class FooterText( DraftStateMixin, @@ -128,4 +172,4 @@ class FooterText( return {"footer_text": self.body} class Meta(TranslatableMixin.Meta): - verbose_name_plural = "Footer Text" \ No newline at end of file + verbose_name_plural = "Footer Text" diff --git a/innovedus_cms/base/templates/base/includes/banner_snippets.html b/innovedus_cms/base/templates/base/includes/banner_snippets.html new file mode 100644 index 0000000..52f6f39 --- /dev/null +++ b/innovedus_cms/base/templates/base/includes/banner_snippets.html @@ -0,0 +1,27 @@ +{% load wagtailimages_tags banner_tags %} + +{% get_banners banner_key as banners %} +{% if banners %} +
+{% endif %} diff --git a/innovedus_cms/base/templates/base/includes/home_banner.html b/innovedus_cms/base/templates/base/includes/home_banner.html new file mode 100644 index 0000000..3490cd2 --- /dev/null +++ b/innovedus_cms/base/templates/base/includes/home_banner.html @@ -0,0 +1,20 @@ +{% load wagtailimages_tags banner_tags %} + +{% get_banners "home" first=True as banner %} +{% if banner %} + +{% endif %} diff --git a/innovedus_cms/base/templatetags/banner_tags.py b/innovedus_cms/base/templatetags/banner_tags.py new file mode 100644 index 0000000..3b39814 --- /dev/null +++ b/innovedus_cms/base/templatetags/banner_tags.py @@ -0,0 +1,15 @@ +from django import template + +from base.models import BannerSnippet + +register = template.Library() + + +@register.simple_tag +def get_banners(key=None, first=False): + banners = BannerSnippet.objects.filter(is_active=True, image__isnull=False) + if key: + banners = banners.filter(key=key) + if first: + return banners.first() + return banners diff --git a/innovedus_cms/home/migrations/0004_articlepage_not_news.py b/innovedus_cms/home/migrations/0004_articlepage_not_news.py new file mode 100644 index 0000000..5bf324e --- /dev/null +++ b/innovedus_cms/home/migrations/0004_articlepage_not_news.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-01-09 05:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0003_categorypage_latestpage_trendingpage_articlepage_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='articlepage', + name='not_news', + field=models.BooleanField(default=False, help_text='不列入最新消息區塊', verbose_name='Not News'), + ), + ] diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py index f062e57..1e5f6b0 100644 --- a/innovedus_cms/home/models.py +++ b/innovedus_cms/home/models.py @@ -18,8 +18,9 @@ def _get_env_int(name, default): except ValueError: return default +NEWS_SIZE = _get_env_int("HOMEPAGE_NEWS_SIZE", 8) # Default to 8 articles in news layouts BLOCK_SIZE = _get_env_int("HOMEPAGE_BLOCK_SIZE", 5) # Default to 5 articles in block layout -HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 8) # Default to 8 articles in horizon layout +HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 4) # Default to 8 articles in horizon layout PAGE_SIZE = _get_env_int("HOMEPAGE_PAGE_SIZE", 10) # Default to 10 articles per page for pagination # Mixin for Category-related functionality @@ -85,8 +86,8 @@ class CategoryMixin: # No request means no pagination (e.g., homepage) return { "title": latest_page.title, - "items": ArticlePage.objects.live().order_by("-date")[ - :BLOCK_SIZE + "items": ArticlePage.objects.filter(not_news=False).live().order_by("-date")[ + :NEWS_SIZE ], "url": latest_page.url, } @@ -154,21 +155,24 @@ class HomePage(Page, CategoryMixin): } latest_section = self.get_latest_articles().copy() - latest_section["layout"] = "block" + # latest_section["layout"] = "block" sections["top_section"].append(latest_section) + # NOT NEED TRENDING SECTION CURRENTLY + # ------------------------------------------------------------------ # Exclude latest articles from trending section - latest_items = latest_section.get("items", []) - if hasattr(latest_items, "values_list"): - latest_ids = list(latest_items.values_list("id", flat=True)) - else: - latest_ids = [item.id for item in latest_items] + # latest_items = latest_section.get("items", []) + # if hasattr(latest_items, "values_list"): + # latest_ids = list(latest_items.values_list("id", flat=True)) + # else: + # latest_ids = [item.id for item in latest_items] - trending_section = self.get_trending_articles( - exclude_ids=latest_ids - ).copy() - trending_section["layout"] = "horizon" - sections["top_section"].append(trending_section) + # trending_section = self.get_trending_articles( + # exclude_ids=latest_ids + # ).copy() + # trending_section["layout"] = "horizon" + # sections["top_section"].append(trending_section) + # ------------------------------------------------------------------ # Build category sections categories = CategoryPage.objects.child_of(self).live().in_menu() @@ -271,6 +275,7 @@ class ArticlePage(Page): use_json_field=True, ) trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示") + not_news = models.BooleanField("Not News", default=False, help_text="不列入最新消息區塊") tags = ClusterTaggableManager(through="home.ArticlePageTag", blank=True) search_fields = Page.search_fields + [ @@ -281,6 +286,7 @@ class ArticlePage(Page): content_panels = Page.content_panels + [ FieldPanel("trending"), + FieldPanel("not_news"), FieldPanel("cover_image"), FieldPanel("banner_image"), FieldPanel("date"), diff --git a/innovedus_cms/home/static/css/block_list.css b/innovedus_cms/home/static/css/block_list.css new file mode 100644 index 0000000..a29d2e2 --- /dev/null +++ b/innovedus_cms/home/static/css/block_list.css @@ -0,0 +1,79 @@ +.block-list-wrap { + position: relative; +} + +.block-list-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 36px; + height: 36px; + border: 0; + border-radius: 8px; + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18); + cursor: pointer; +} + +.block-list-arrow[data-dir="left"] { + left: 0; +} + +.block-list-arrow[data-dir="right"] { + right: 0; +} + +.block-list-arrow.is-hidden { + opacity: 0; + pointer-events: none; +} + +.block-list { + display: flex; + align-items: stretch; + gap: 20px; + overflow-x: auto; + scroll-snap-type: x mandatory; + list-style: none; + margin: 24px 0; + padding: 12px 48px; +} + +.block-list li { + flex: 0 0 200px; + overflow: hidden; + scroll-snap-align: start; +} + +.block-list li a { + display: flex; + flex-direction: column; + gap: 12px; + color: inherit; + text-decoration: none; + padding: 16px; +} + +.block-list li img { + width: 100%; + height: 200px; + /* object-fit: contain; */ + display: block; + /* background: #f5f5f5; */ + /* border-radius: 8px; */ +} + +.block-list li a:hover, +.block-list li a:focus { + transform: translateY(-2px); +} + +.block-list .empty { + flex: 1 0 auto; + text-align: center; + padding: 24px; + color: #666; + background: #fafafa; + border: 1px dashed #ddd; + border-radius: 12px; +} diff --git a/innovedus_cms/home/static/css/home.css b/innovedus_cms/home/static/css/home.css new file mode 100644 index 0000000..e92c9f7 --- /dev/null +++ b/innovedus_cms/home/static/css/home.css @@ -0,0 +1,74 @@ +.home-page { + max-width: 890px; + margin: 0 auto; + padding: 0 16px; +} + +.home-hero-band { + background-color: #0e1b42; + color: #ffffff; + padding-bottom: 86px; +} + +.home-hero-band a { + color: #ffffff; +} + +.template-homepage .site-header { + background-color: #0e1b42; + color: #ffffff; +} + +.template-homepage .header-inner { + max-width: 1028px; + margin: 0 auto; + /* padding: 0 16px; */ +} + +.template-homepage .main-menu-link { + color: #ffffff; +} + +.template-homepage .header-search .search-input { + /* border-color: #ffffff; */ + background-color: rgba(255, 255, 255, 0.4); +} + +.template-homepage .header-search .search-icon { + color: #ffffff; +} + +.template-homepage .header-search input[type="search"] { + color: #ffffff; +} + +.list-title { + align-items: center; + margin: 10px 0; +} + +.block-title { + display: inline-block; + width: 197px; + height: 87px; + vertical-align: middle; + font-size: 20px; + font-weight: 700; +} + +.block-title span { + padding-left: 21px; + line-height: 87px; +} + +.block-title-divider { + display: inline-flex; + width: 28px; + height: 1px; + transform: translate(-4px, -4px); +} + +.more-link { + text-decoration: none; + font-size: 16px; +} \ No newline at end of file diff --git a/innovedus_cms/home/static/css/horizontal_list.css b/innovedus_cms/home/static/css/horizontal_list.css new file mode 100644 index 0000000..b548f5e --- /dev/null +++ b/innovedus_cms/home/static/css/horizontal_list.css @@ -0,0 +1,112 @@ +.horizontal-list-wrap { + position: relative; +} + +.horizontal-list-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + width: 52px; + height: 52px; + border: 0; + border-radius: 8px; + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18); + cursor: pointer; +} + +.horizontal-list-arrow[data-dir="left"] { + left: 0; +} + +.horizontal-list-arrow[data-dir="right"] { + right: 0; +} + +.horizontal-list-arrow-icon { + width: 18px; + height: 34px; + display: block; +} + +.horizontal-list-arrow[data-dir="right"] .horizontal-list-arrow-icon { + transform: rotate(180deg); +} + +.horizontal-list-arrow.is-hidden { + opacity: 0; + pointer-events: none; +} + +.horizontal-list { + display: flex; + align-items: stretch; + gap: 24px; + overflow-x: auto; + scroll-snap-type: x mandatory; + list-style: none; + margin: 24px 0; + padding: 12px 48px; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.horizontal-list::-webkit-scrollbar { + display: none; +} + +.horizontal-list li { + flex: 0 0 200px; + overflow: hidden; + scroll-snap-align: start; +} + +.horizontal-list li a { + display: flex; + flex-direction: column; + gap: 12px; + color: inherit; + text-decoration: none; +} + +.horizontal-list li img { + width: 100%; + height: 200px; + display: block; + margin: 0 3px; +} + +.horizontal-list li a:hover, +.horizontal-list li a:focus { + transform: translateY(-2px); +} + +.horizontal-list li a span { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + font-size: 20px; +} + +.horizontal-list .article-title { + font-size: 20px; +} + +.horizontal-list .article-intro { + font-size: 16px; +} + +.horizontal-list .empty { + flex: 1 0 auto; + text-align: center; + padding: 24px; + color: #666; + background: #fafafa; + border: 1px dashed #ddd; + border-radius: 12px; +} diff --git a/innovedus_cms/home/static/css/news_list.css b/innovedus_cms/home/static/css/news_list.css new file mode 100644 index 0000000..798d250 --- /dev/null +++ b/innovedus_cms/home/static/css/news_list.css @@ -0,0 +1,182 @@ +.news-title, .more-news-title { + background-color: #ffffff; + color: #0e1b42; +} + +.news-hero { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: + "header header" + "image meta" + "image intro" + "image body"; + gap: 16px; + align-items: start; +} + +.news-hero .news-hero-header { + grid-area: header; + display: flex; + align-items: center; + gap: 12px; +} + +.news-hero .list-title { + grid-area: title; + max-width: 310px; + flex: 0 1 310px; + display: block; +} + +.news-hero .fist-news-title { + grid-area: head; + max-width: 580px; + font-size: 40px; + font-weight: 400; + color: #eb9f13; + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.news-hero .first-news-image { + grid-area: image; + position: relative; + border-radius: 10px; + overflow: hidden; +} + +.news-hero .first-news-image::after { + content: ""; + position: absolute; + left: 0px; + right: 0px; + bottom: 0px; + height: 25px; + background: url("../img/picfrm_b480.png") no-repeat left bottom / cover; + background-size: 480px 25px; + pointer-events: none; +} + +.news-hero .first-news-image img { + display: block; + width:480px; + height:293px; + object-fit:cover; + border-radius: 10px; +} + +.news-hero .fist-news-date { + grid-area: meta; + color: rgba(255, 255, 255, 0.4); +} + +.news-hero .first-news-intro { + grid-area: intro; + font-size: 20px; +} + +.news-hero .first-news-body { + grid-area: body; + font: 12px; +} + +.news-list-items { + display: contents; +} + +.news-list-lower { + margin-top: 24px; + display: flex; + flex-wrap: wrap; + gap: 30px; +} + +.news-list-items a { + display: flex; + flex-direction: column; + gap: 12px; + width: 200px; + text-decoration: none; +} + +.news-list-thumb { + position: relative; + width: 194px; + height: 133px; + border-radius: 10px; + overflow: hidden; +} + +.news-list-thumb::after { + content: ""; + position: absolute; + left: 0px; + right: 0px; + bottom: 0px; + height: 25px; + background: url("../img/picfrm_o194.png") no-repeat left bottom / cover; + background-size: 194px 25px; + pointer-events: none; +} + +.news-list-thumb img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.news-list-items a span{ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.news-list-items .article-title { + font-size: 20px; +} + +.news-list-items .article-date { + color: rgba(255, 255, 255, 0.4); +} + +.more-news { + width: 200px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.more-news .article-title { + font-size: 20px; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; +} + +@media (max-width: 768px) { + .news-hero { + grid-template-columns: 1fr; + grid-template-areas: + "title" + "image" + "head" + "meta" + "intro" + "body"; + } + + .news-hero .news-hero-header { + display: contents; + } + + .news-hero .list-title { + max-width: none; + } +} diff --git a/innovedus_cms/home/static/img/picfrm_b139.png b/innovedus_cms/home/static/img/picfrm_b139.png new file mode 100644 index 0000000..b5d4f6c Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b139.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b194.png b/innovedus_cms/home/static/img/picfrm_b194.png new file mode 100644 index 0000000..bada8a3 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b194.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b300.png b/innovedus_cms/home/static/img/picfrm_b300.png new file mode 100644 index 0000000..d9d2f5a Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b300.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b318.png b/innovedus_cms/home/static/img/picfrm_b318.png new file mode 100644 index 0000000..4b18084 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b318.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b426.png b/innovedus_cms/home/static/img/picfrm_b426.png new file mode 100644 index 0000000..25675e1 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b426.png differ diff --git a/innovedus_cms/home/static/img/picfrm_b480.png b/innovedus_cms/home/static/img/picfrm_b480.png new file mode 100644 index 0000000..494a2d3 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_b480.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o139.png b/innovedus_cms/home/static/img/picfrm_o139.png new file mode 100644 index 0000000..916760f Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o139.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o194.png b/innovedus_cms/home/static/img/picfrm_o194.png new file mode 100644 index 0000000..b2f646a Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o194.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o300.png b/innovedus_cms/home/static/img/picfrm_o300.png new file mode 100644 index 0000000..045ef82 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o300.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o318.png b/innovedus_cms/home/static/img/picfrm_o318.png new file mode 100644 index 0000000..44ab13a Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o318.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o426.png b/innovedus_cms/home/static/img/picfrm_o426.png new file mode 100644 index 0000000..48ca2c7 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o426.png differ diff --git a/innovedus_cms/home/static/img/picfrm_o480.png b/innovedus_cms/home/static/img/picfrm_o480.png new file mode 100644 index 0000000..0406a77 Binary files /dev/null and b/innovedus_cms/home/static/img/picfrm_o480.png differ diff --git a/innovedus_cms/home/static/js/block_list.js b/innovedus_cms/home/static/js/block_list.js new file mode 100644 index 0000000..edd7129 --- /dev/null +++ b/innovedus_cms/home/static/js/block_list.js @@ -0,0 +1,45 @@ +(function () { + function initBlockList(block) { + var list = block.querySelector('.block-list'); + var left = block.querySelector('[data-dir="left"]'); + var right = block.querySelector('[data-dir="right"]'); + + if (!list || !left || !right) { + return; + } + + function getScrollAmount() { + return Math.max(200, Math.floor(list.clientWidth * 0.8)); + } + + function updateArrows() { + var maxScroll = list.scrollWidth - list.clientWidth; + var hasOverflow = maxScroll > 1; + var atStart = list.scrollLeft <= 1; + var atEnd = list.scrollLeft >= maxScroll - 1; + + left.classList.toggle('is-hidden', !hasOverflow || atStart); + right.classList.toggle('is-hidden', !hasOverflow || atEnd); + } + + left.addEventListener('click', function () { + list.scrollBy({ left: -getScrollAmount(), behavior: 'smooth' }); + }); + + right.addEventListener('click', function () { + list.scrollBy({ left: getScrollAmount(), behavior: 'smooth' }); + }); + + list.addEventListener('scroll', function () { + window.requestAnimationFrame(updateArrows); + }); + + window.addEventListener('resize', updateArrows); + updateArrows(); + } + + document.addEventListener('DOMContentLoaded', function () { + var blocks = document.querySelectorAll('[data-block-list]'); + blocks.forEach(initBlockList); + }); +})(); diff --git a/innovedus_cms/home/static/js/horizontal_list.js b/innovedus_cms/home/static/js/horizontal_list.js new file mode 100644 index 0000000..3a52dfb --- /dev/null +++ b/innovedus_cms/home/static/js/horizontal_list.js @@ -0,0 +1,45 @@ +(function () { + function initHorizontalList(block) { + var list = block.querySelector('.horizontal-list'); + var left = block.querySelector('[data-dir="left"]'); + var right = block.querySelector('[data-dir="right"]'); + + if (!list || !left || !right) { + return; + } + + function getScrollAmount() { + return Math.max(200, Math.floor(list.clientWidth * 0.8)); + } + + function updateArrows() { + var maxScroll = list.scrollWidth - list.clientWidth; + var hasOverflow = maxScroll > 1; + var atStart = list.scrollLeft <= 1; + var atEnd = list.scrollLeft >= maxScroll - 1; + + left.classList.toggle('is-hidden', !hasOverflow || atStart); + right.classList.toggle('is-hidden', !hasOverflow || atEnd); + } + + left.addEventListener('click', function () { + list.scrollBy({ left: -getScrollAmount(), behavior: 'smooth' }); + }); + + right.addEventListener('click', function () { + list.scrollBy({ left: getScrollAmount(), behavior: 'smooth' }); + }); + + list.addEventListener('scroll', function () { + window.requestAnimationFrame(updateArrows); + }); + + window.addEventListener('resize', updateArrows); + updateArrows(); + } + + document.addEventListener('DOMContentLoaded', function () { + var blocks = document.querySelectorAll('[data-horizontal-list]'); + blocks.forEach(initHorizontalList); + }); +})(); diff --git a/innovedus_cms/home/templates/home/home_page.html b/innovedus_cms/home/templates/home/home_page.html index 718cf8d..6af0092 100644 --- a/innovedus_cms/home/templates/home/home_page.html +++ b/innovedus_cms/home/templates/home/home_page.html @@ -1,20 +1,33 @@ {% extends "base.html" %} +{% load static %} {% block body_class %}template-homepage{% endblock %} +{% block extra_css %} + + + + +{% endblock %} {% block content %} -{% with top_section=sections.top_section %} -
- {% endif %}
- {{ article.title }}
-
-
+ {% endif %}
+ {{ article.title }}
+
+
- {% endif %}
- {{ article.title }}
-
-
+ {% endif %}
+ {{ article.title }}
+ {{ article.intro }}
+
+
+ {% endif %}
+