@fireng/core
Version:
Core utilities for Fireng Angular responsive library.
193 lines (186 loc) • 8.06 kB
JavaScript
import { DOCUMENT } from '@angular/common';
import * as i0 from '@angular/core';
import { InjectionToken, inject, signal, computed, Injectable } from '@angular/core';
import { fromEvent, auditTime } from 'rxjs';
const DEFAULT_BREAKPOINTS = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1400,
};
const FIRENG_BREAKPOINTS = new InjectionToken('FIRENG_BREAKPOINTS_TOKEN', {
factory: () => DEFAULT_BREAKPOINTS,
});
class FirengScreenService {
document = inject(DOCUMENT);
window = this.document.defaultView || undefined;
// signal to store the current window width
_windowWidth = signal(0);
windowWidth = this._windowWidth.asReadonly();
// signal to store the current window height
_windowHeight = signal(0);
windowHeight = this._windowHeight.asReadonly();
// Signal to store breakpoint and this allows for runtime updates to breakpoints
_breakpoint;
// Computed signal to get sorted breakpoints
_sortedBreakpoints = computed(() => {
const breakpointsEntries = Object.entries(this._breakpoint() || {}).sort((a, b) => a[1] - b[1]);
return Object.fromEntries(breakpointsEntries);
});
// Computed signal for current breakpoint
currentBreakpoint = computed(() => {
const width = this._windowWidth();
const breakpoints = this._sortedBreakpoints();
// taking first first key of breakpoints as default active breakpoint
let activeBreakpointKey = Object.keys(breakpoints)[0];
for (const [breakpointKey, breakpointValue] of Object.entries(breakpoints)) {
if (width >= breakpointValue) {
activeBreakpointKey = breakpointKey;
}
else {
break; // Exit loop once we find the first breakpoint that is larger than the width
}
}
return activeBreakpointKey;
});
// Computed signal for cascading breakpoints
activeBreakpoints = computed(() => {
const currentBreakpoint = this.currentBreakpoint();
const breakpoints = this._sortedBreakpoints();
const activeNames = [];
for (const [breakpointKey, breakpointValue] of Object.entries(breakpoints)) {
activeNames.push(breakpointKey);
if (breakpointKey === currentBreakpoint) {
break;
}
}
return activeNames;
});
constructor() {
this._breakpoint = signal(inject(FIRENG_BREAKPOINTS));
if (this.window) {
this._windowWidth.set(this.window.innerWidth);
this._windowHeight.set(this.window.innerHeight);
fromEvent(this.window, 'resize')
.pipe(auditTime(100))
.subscribe(() => {
this._windowWidth.set(this.window?.innerWidth || 0);
this._windowHeight.set(this.window?.innerHeight || 0);
});
}
else {
console.warn('FirengScreenService: No window object found. Screen service may not work as expected.');
}
}
/**
* Checks if the current screen width matches the specified breakpoint.
* @param breakpointName The name of the breakpoint (e.g., 'xs', 'md').
* @returns A signal indicating if the current screen width matches the specified breakpoint.
*/
isBreakpoint(breakpointName) {
return computed(() => breakpointName === this.currentBreakpoint());
}
/**
* Checks if the current screen size is at or above a specific breakpoint.
* @param breakpointName The name of the breakpoint (e.g., 'sm', 'md').
* @returns A signal indicating if the current screen is at or above the breakpoint.
*/
isBreakpointUp(breakpointName) {
return computed(() => {
const breakpoints = this._sortedBreakpoints();
const targetIndex = Object.keys(breakpoints).indexOf(breakpointName);
const currentIndex = Object.keys(breakpoints).indexOf(this.currentBreakpoint());
return (targetIndex >= currentIndex && targetIndex >= 0 && currentIndex >= 0);
});
}
/**
* Checks if the current screen size is below a specific breakpoint.
* @param breakpointName The name of the breakpoint (e.g., 'sm', 'md').
* @returns A signal indicating if the current screen is below the breakpoint.
*/
isBreakpointDown(breakpointName) {
return computed(() => {
const breakpoints = this._sortedBreakpoints();
const targetIndex = Object.keys(breakpoints).indexOf(breakpointName);
const currentIndex = Object.keys(breakpoints).indexOf(this.currentBreakpoint());
return (currentIndex < targetIndex && targetIndex >= 0 && currentIndex >= 0);
});
}
/**
* Allows users to update breakpoints dynamically at runtime.
* This will immediately update the `currentBreakpoint` and `activeBreakpoints` signals.
* @param newBreakpoints An array of new breakpoint definitions.
*/
setBreakpoints(newBreakpoints) {
const keys = new Set();
for (const [key, value] of Object.entries(newBreakpoints)) {
if (typeof value !== 'number' || isNaN(value) || value < 0) {
console.warn(`Invalid breakpoint value for "${key}": ${value}`);
return;
}
else if (keys.has(key)) {
console.warn(`Duplicate breakpoint key found: "${key}"`);
return;
}
keys.add(key);
}
this._breakpoint.set(newBreakpoints);
console.log('FirengScreenService: Breakpoints updated successfully.');
}
isPortrait = computed(() => {
return this._windowHeight() > this._windowWidth();
});
/**
* Resolves a responsive value from a map based on current active breakpoints.
*
* It applies cascading logic: the value for the largest active breakpoint in `valueMap` is returned.
* If no match is found, the `fallback` value is returned.
*
* @template T The type of the value (e.g., `number`, `string`).
* @param valueMap An object mapping breakpoint names to values (e.g., `{ xs: 5, md: 10 }`).
* @param fallback An optional default value if no matching breakpoint is found.
* @returns A signal emitting the resolved value, or `fallback`, or `undefined`.
*
* @example
* // Get items per page: 5 for xs, 10 for md and up, default 5
* const itemsPerPage = this.screenService.resolveBreakpointValue({ xs: 5, md: 10 }, 5);
*/
resolveBreakpointValue(valueMap, fallback) {
return computed(() => {
const activeBreakpoints = this.activeBreakpoints();
let selectedValue;
for (const breakpoint of activeBreakpoints) {
if (valueMap.hasOwnProperty(breakpoint)) {
selectedValue = valueMap[breakpoint];
}
}
if (selectedValue === undefined) {
return fallback;
}
return selectedValue;
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FirengScreenService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FirengScreenService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FirengScreenService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [] });
function provideFirengBreakpoints(breakpoints) {
return [
{
provide: FIRENG_BREAKPOINTS,
useValue: breakpoints,
},
];
}
/**
* Generated bundle index. Do not edit.
*/
export { FIRENG_BREAKPOINTS, FirengScreenService, provideFirengBreakpoints };
//# sourceMappingURL=fireng-core.mjs.map