Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/assets/stylesheets/landing_page/_nav.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.qul-nav-link.qul-nav-active {
font-weight: 600;
text-decoration: underline;
text-decoration-thickness: 2px;
text-underline-offset: 8px;
}


5 changes: 5 additions & 0 deletions app/assets/stylesheets/landing_page/_tool_cards.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ a.tool-card-with-bg,
text-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
}

a.tw-group:hover .tool-card-with-bg {
transform: translateY(-5px);
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.35);
}

.tool-card-with-bg{
height: 500px;
background-repeat: no-repeat !important;
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/landing_page/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ body {
@import "tabs";
@import "tool_cards";
@import "resource_show";
@import "nav";

.qul-tag {
font-family: Inter;
Expand Down
6 changes: 6 additions & 0 deletions app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ application.register("resizeable", ResizeableController);
import TooltipController from "./tooltip_controller.js";
application.register("tooltip", TooltipController);

import StickyHeaderController from "./sticky_header_controller.js";
application.register("sticky-header", StickyHeaderController);

import SplineViewerController from "./spline_viewer_controller.js";
application.register("spline-viewer", SplineViewerController);

import TurboFrameLoadingController from "./turbo_frame_loading_controller.js";
application.register("turbo-frame-loading", TurboFrameLoadingController);

Expand Down
88 changes: 57 additions & 31 deletions app/javascript/controllers/spline_viewer_controller.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,68 @@
import {Controller} from "@hotwired/stimulus";
import ScreenSize from "../utils/screen-size";

export default class extends Controller {
static values = {
url: String,
width: Number,
height: Number,
viewerClass: String,
}

connect() {
this.device = new ScreenSize();
// this.adjustCanvasSize();
this.mounted = false;

if (!window.matchMedia("(min-width: 1024px)").matches) return;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;

this.observer = new IntersectionObserver(
(entries) => {
if (entries.some((e) => e.isIntersecting)) {
this.mount();
this.observer?.disconnect();
this.observer = null;
}
},
{ root: null, rootMargin: "200px", threshold: 0.01 }
);

window.addEventListener('resize', () => {
// setTimeout(() => this.adjustCanvasSize(), 100);
});
this.observer.observe(this.element);
}

adjustCanvasSize() {
const canvas = this.element.querySelector("canvas");
disconnect() {
this.observer?.disconnect();
this.observer = null;
}

const {scale, desktopScale, scaleLargeDesktop, mobileScale, marginLeft} = this.element.dataset;
if(scale && scale === '1' || !canvas) {
return
}
const d = this.device;

if(d.isMobile() && mobileScale){
canvas.style.transform = `scale(${parseFloat(mobileScale)})`;
console.log("========= mobile", mobileScale)

} else if (d.isDesktop()) {
// Desktop
let s = parseFloat(desktopScale || scale || 1.5);
canvas.style.transform = `scale(${s})`;
//canvas.style.marginLeft = `${marginLeft||'-40px'}`;
console.log("========= desktop", s)
} else if (d.isLargeDesktop()) {
// Large Desktop
let largeScreeenScale = parseFloat(scaleLargeDesktop || scale || 1);
console.log("========= largeScreeenScale", largeScreeenScale)

canvas.style.transform = `scale(${largeScreeenScale})`;
// canvas.style.marginLeft = `-60px`;
async mount() {
if (this.mounted) return;
this.mounted = true;

await this.loadViewer();

const el = document.createElement("spline-viewer");
el.setAttribute("url", this.urlValue);

if (this.hasWidthValue) el.setAttribute("width", String(this.widthValue));
if (this.hasHeightValue) el.setAttribute("height", String(this.heightValue));
if (this.hasViewerClassValue && this.viewerClassValue) el.setAttribute("class", this.viewerClassValue);

this.element.replaceChildren(el);
}

loadViewer() {
if (!window.__qulSplineViewerLoadPromise) {
window.__qulSplineViewerLoadPromise = new Promise((resolve, reject) => {
const script = document.createElement("script");
script.type = "module";
script.src = "https://unpkg.com/@splinetool/viewer/build/spline-viewer.js";
script.async = true;
script.defer = true;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}

return window.__qulSplineViewerLoadPromise;
}
}
87 changes: 87 additions & 0 deletions app/javascript/controllers/sticky_header_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
static targets = ["header", "spacer"];
static values = {
offset: { type: Number, default: 40 },
transparent: { type: Boolean, default: false }
};

connect() {
this.isStuck = false;
this.lastY = window.scrollY || 0;
this.headerHeight = this.headerTarget.offsetHeight || 0;
this.spacerTarget.style.height = `${this.headerHeight}px`;

this.onScroll = this.onScroll.bind(this);
this.onResize = this.onResize.bind(this);

window.addEventListener("scroll", this.onScroll, { passive: true });
window.addEventListener("resize", this.onResize, { passive: true });

this.apply(window.scrollY || 0, true);
}

disconnect() {
window.removeEventListener("scroll", this.onScroll);
window.removeEventListener("resize", this.onResize);
}

onResize() {
this.headerHeight = this.headerTarget.offsetHeight || 0;
this.spacerTarget.style.height = `${this.headerHeight}px`;
if (this.isStuck) this.showSpacer();
}

onScroll() {
const y = window.scrollY || 0;
this.apply(y, false);
this.lastY = y;
}

apply(y, immediate) {
const shouldStick = y > this.offsetValue;
if (shouldStick === this.isStuck) return;
this.isStuck = shouldStick;

if (shouldStick) {
this.showSpacer();
this.headerTarget.classList.add("tw-fixed", "tw-top-0", "tw-start-0", "tw-w-full", "tw-z-50", "tw-shadow-lg");
this.headerTarget.classList.add("tw-bg-white", "tw-backdrop-blur", "tw-border-b", "tw-border-black/10");
this.headerTarget.style.transform = "translateY(-12px)";
this.headerTarget.style.opacity = "0";
if (immediate) {
this.headerTarget.style.transform = "";
this.headerTarget.style.opacity = "";
} else {
requestAnimationFrame(() => {
this.headerTarget.style.transform = "translateY(0)";
this.headerTarget.style.opacity = "1";
});
clearTimeout(this.clearStylesTimeout);
this.clearStylesTimeout = setTimeout(() => {
if (!this.isStuck) return;
this.headerTarget.style.transform = "";
this.headerTarget.style.opacity = "";
}, 260);
}
} else {
this.hideSpacer();
this.headerTarget.classList.remove("tw-fixed", "tw-top-0", "tw-start-0", "tw-w-full", "tw-z-50", "tw-shadow-lg");
this.headerTarget.classList.remove("tw-bg-white", "tw-backdrop-blur", "tw-border-b", "tw-border-black/10");
clearTimeout(this.clearStylesTimeout);
this.headerTarget.style.transform = "";
this.headerTarget.style.opacity = "";
}
}

showSpacer() {
this.spacerTarget.classList.remove("tw-hidden");
}

hideSpacer() {
this.spacerTarget.classList.add("tw-hidden");
}
}


6 changes: 3 additions & 3 deletions app/presenters/application_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ def meta_tags
end

def meta_title
'Quranic Universal Library'
'QUL | Quranic Universal Library: Open-Source Muslim Developer Tools'
end

def meta_description
'A comprehensive collection of Quranic digital resources'
'The ultimate open-source toolkit for Muslim developers. Access Quranic APIs, recitations, Mushaf layouts, translations, and metadata to build your next Islamic project.'
end

def meta_keywords
'quran, islamic tools, muslim developers, quran api, quranic library'
'Quranic API, Islamic Open Source, Quranic Data, Muslim Dev Toolkit, Quran Dataset JSON.'
end

def og_image
Expand Down
1 change: 1 addition & 0 deletions app/presenters/resource_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def meta_description
if show?
resource_key = resource.resource_type.tr('-', '_').to_sym
card = downloadable_resource_cards[resource_key]

if card
card.description
end
Expand Down
1 change: 1 addition & 0 deletions app/presenters/word_mistake_update_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ def parse_mistake_key(key)
[word_id, char_start, char_end]
end
end

1 change: 1 addition & 0 deletions app/presenters/word_mistakes_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,4 @@ def load_mistakes(words)
.group_by(&:word_id)
end
end

1 change: 1 addition & 0 deletions app/views/api/v1/tafsirs/by_range.json.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ end




1 change: 1 addition & 0 deletions app/views/api/v1/tafsirs/for_ayah.json.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ json.metadata do
end



1 change: 1 addition & 0 deletions app/views/ayah/_recitation.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@
<% end %>



1 change: 1 addition & 0 deletions app/views/ayah/_theme.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@
<% end %>



1 change: 1 addition & 0 deletions app/views/ayah/_transliteration.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@
<% end %>



4 changes: 2 additions & 2 deletions app/views/card/_card-title.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<p class="tw-text-xl tw-font-semibold tw-text-pretty">
<h3 class="<%= ['tw-text-xl tw-font-semibold tw-text-pretty', local_assigns[:class_name]].compact.join(' ') %>">
<%= title %>
</p>
</h3>
6 changes: 3 additions & 3 deletions app/views/card/_card_content.html.erb
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<div class="tw-gap-4 tw-h-full tw-flex tw-flex-col tw-justify-between tw-w-full">
<div class="tw-flex tw-flex-col tw-justify-between tw-h-full">
<div class="tw-flex tw-flex-col tw-gap-2">
<%= render 'card/card-title', title: local_assigns[:title] || card.name || card.title %>
<%= render 'card/card-title', title: local_assigns[:title] || card.name || card.title, class_name: local_assigns[:tool] ? 'tw-text-2xl tw-font-bold' : nil %>
<%= render 'card/card-description', description: card.humanize_cardinality_type || card.description %>
</div>

<% if local_assigns[:show_learn_more] %>
<div class="tw-mt-8">
<%= render 'card/learn-more-button' %>
<%= render 'card/learn-more-button', text: local_assigns[:learn_more_text] %>
</div>
<% end %>

Expand All @@ -29,7 +29,7 @@
<% if card.icon.start_with?('fa-') %>
<i class="fa tw-text-white tw-text-[150px] md:tw-text-[180px] lg:tw-text-[200px] <%= card.icon %> tw-opacity-90 group-hover:tw-opacity-100 tw-transition-opacity"></i>
<% else %>
<%= image_tag "card-icon/#{card.icon}", alt: "#{card.name || card.title} icon", class: "tw-max-w-full tw-h-auto tw-opacity-90 group-hover:tw-opacity-100 tw-transition-opacity" %>
<%= image_tag "card-icon/#{card.icon}", alt: "#{card.name || card.title}", class: "tw-max-w-full tw-h-auto tw-opacity-90 group-hover:tw-opacity-100 tw-transition-opacity" %>
<% end %>
<% end %>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/views/card/_card_footer.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

<div class="tw-flex tw-items-center tw-justify-between">
<% if local_assigns[:show_stats] && card.respond_to?(:count) && card.count.to_i > 0 %>
<div class="tw-text-white tw-font-bold tw-opacity-70">
<div class="tw-text-white tw-font-bold tw-bg-black/60 tw-rounded-lg tw-px-3 tw-py-2">
<%= safe_html(card.stats) if card.respond_to?(:stats) %>
</div>
<div class="tw-rounded-full tw-bg-[#E0FAEC] tw-text-[#099a4d] tw-font-bold tw-text-sm tw-px-3 tw-py-1 tw-border-2 tw-border-[#56B599]">
<div class="tw-rounded-full tw-bg-[#E0FAEC] tw-text-[#047857] tw-font-bold tw-text-sm tw-px-3 tw-py-1 tw-border-2 tw-border-[#56B599]">
<%= card.count %>
</div>
<% elsif show_tags %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/card/_learn-more-button.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div class="tw-rounded-full tw-text-sm tw-font-semibold tw-bg-white/40 tw-text-white tw-px-3 tw-py-1.5 tw-inline-block tw-w-fit">
<span>Learn More</span>
<span><%= local_assigns[:text].presence || 'Learn More' %></span>
</div>
Loading
Loading