Skip to content

Commit

Permalink
Convert more utils into TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
lahmatiy committed May 24, 2024
1 parent f78049b commit c6f6b2d
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 110 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { resolveDarkmodeValue } from '../darkmode.js';
import { InitValue, resolveDarkmodeValue } from '../darkmode.js';

const styles = {
type Styles = Record<string, string>;
type SavedStyles = Record<string, [string, string]>;
type Config = {
darkmode?: InitValue,
darkmodePersistent?: boolean;
};

const styles: Styles = {
'font-family': 'Tahoma, Verdana, Arial, sans-serif',
'font-size': '16px',
'line-height': '1.6',
Expand All @@ -12,14 +19,14 @@ const styles = {
'transition-duration': '.25s',
'transition-timing-function': 'ease-in'
};
const darkmodeStyles = {
const darkmodeStyles: Styles = {
'--discovery-background-color': '#242424',
'--discovery-color': '#cccccc'
};
const knowContainer = new WeakSet();
const containerBeforeSetStyle = new WeakMap();
const knowContainer = new WeakSet<HTMLElement>();
const containerBeforeSetStyle = new WeakMap<HTMLElement, SavedStyles>();

function saveContainerStyleProp(container, prop, styles) {
function saveContainerStyleProp(container: HTMLElement, prop: string, styles: SavedStyles) {
if (prop in styles === false) {
styles[prop] = [
container.style.getPropertyValue(prop),
Expand All @@ -28,15 +35,15 @@ function saveContainerStyleProp(container, prop, styles) {
}
}

export function applyContainerStyles(container, config) {
export function applyContainerStyles(container: HTMLElement, config: Config) {
config = config || {};

if (!containerBeforeSetStyle.has(container)) {
containerBeforeSetStyle.set(container, Object.create(null));
}

const darkmode = resolveDarkmodeValue(config.darkmode, config.darkmodePersistent);
const containerStyles = containerBeforeSetStyle.get(container);
const containerStyles = containerBeforeSetStyle.get(container) ?? {};

for (const [prop, value] of Object.entries(styles)) {
if (knowContainer.has(container) || !/^transition/.test(prop)) {
Expand All @@ -60,15 +67,15 @@ export function applyContainerStyles(container, config) {
return darkmode;
}

export function rollbackContainerStyles(container) {
export function rollbackContainerStyles(container: HTMLElement) {
if (containerBeforeSetStyle.has(container)) {
const containerStyles = containerBeforeSetStyle.get(container);
const containerStyles = containerBeforeSetStyle.get(container) ?? {};

for (const [prop, value] of Object.entries(containerStyles)) {
container.style.setProperty(prop, ...value);
}

containerBeforeSetStyle.delete(containerBeforeSetStyle);
containerBeforeSetStyle.delete(container);
knowContainer.delete(container);
}
}
41 changes: 28 additions & 13 deletions src/core/utils/dom.js → src/core/utils/dom.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
/* eslint-env browser */

export function createElement(tag, attrs, children) {
type EventHandler<Element, Event> = (this: Element, evt: Event) => void;
type Attrs<TagName extends keyof HTMLElementTagNameMap> = {
[key in keyof HTMLElementEventMap as `on${key}`]?: EventHandler<
HTMLElementTagNameMap[TagName],
HTMLElementEventMap[key]
>;
} & {
[key: string]: any | undefined; // TODO: replace "any" with "string"
};

export function createElement<TagName extends keyof HTMLElementTagNameMap>(
tag: TagName,
attrs: Attrs<TagName> | string | null,
children?: (Node | string)[] | string
) {
const el = document.createElement(tag);

if (typeof attrs === 'string') {
Expand All @@ -10,35 +24,35 @@ export function createElement(tag, attrs, children) {
}

for (let attrName in attrs) {
if (hasOwnProperty.call(attrs, attrName)) {
if (attrs[attrName] === undefined) {
if (Object.hasOwn(attrs, attrName)) {
const value = attrs[attrName];

if (typeof value === "undefined") {
continue;
}

if (attrName.startsWith('on')) {
el.addEventListener(attrName.substr(2), attrs[attrName]);
if (typeof value === "function") {
el.addEventListener(attrName.slice(2), value);
} else {
el.setAttribute(attrName, attrs[attrName]);
el.setAttribute(attrName, value);
}
}
}

if (Array.isArray(children)) {
children.forEach(child =>
el.appendChild(child instanceof Node ? child : createText(child))
);
el.append(...children);
} else if (typeof children === 'string') {
el.innerHTML = children;
}

return el;
}

export function createText(text) {
export function createText(text: any) {
return document.createTextNode(String(text));
}

export function createFragment(...children) {
export function createFragment(...children: (Node | string)[]) {
const fragment = document.createDocumentFragment();

children.forEach(child =>
Expand All @@ -61,8 +75,9 @@ export const passiveSupported = (() => {
}
};

window.addEventListener('test', null, options);
window.removeEventListener('test', null, options);
const cb = () => {};
window.addEventListener('test-passive', cb, options);
window.removeEventListener('test-passive', cb);
} catch (err) {}

return passiveSupported;
Expand Down
4 changes: 2 additions & 2 deletions src/core/utils/html.js → src/core/utils/html.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
export function escapeHtml(str) {
export function escapeHtml(str: string) {
return str
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}

export function numDelim(value, escape = true) {
export function numDelim(value: any, escape = true) {
const strValue = escape && typeof value !== 'number'
? escapeHtml(String(value))
: String(value);
Expand Down
7 changes: 0 additions & 7 deletions src/core/utils/id.js

This file was deleted.

7 changes: 7 additions & 0 deletions src/core/utils/id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function randomId() {
return [
performance.timeOrigin.toString(16),
(10000 * performance.now()).toString(16),
String(Math.random().toString(16).slice(2))
].join('-');
}
38 changes: 20 additions & 18 deletions src/core/utils/inject-styles.js → src/core/utils/inject-styles.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { createElement } from './dom.js';

export default function injectStyles(el, styles) {
export type InlineStyle = {
type: 'style' | 'inline';
content: string;
media?: string;
};
export type LinkStyle = {
type: 'link' | 'external';
href: string;
media?: string;
};
export type Style = string | InlineStyle | LinkStyle;

export default async function injectStyles(el: HTMLElement, styles: Style[]) {
const foucFix = createElement('style', null, ':host{display:none}');
const awaitingStyles = new Set();
let readyStyles = Promise.resolve();
const awaitingStyles = new Set<Promise<void>>();

if (Array.isArray(styles)) {
el.append(...styles.map(style => {
Expand All @@ -23,9 +34,9 @@ export default function injectStyles(el, styles) {

case 'link':
case 'external': {
let resolveStyle;
let rejectStyle;
let state = new Promise((resolve, reject) => {
let resolveStyle: () => void;
let rejectStyle: (err?: any) => void;
let state = new Promise<void>((resolve, reject) => {
resolveStyle = resolve;
rejectStyle = reject;
});
Expand All @@ -39,34 +50,25 @@ export default function injectStyles(el, styles) {
onerror(err) {
awaitingStyles.delete(state);
rejectStyle(err);

if (!awaitingStyles.size) {
foucFix.remove();
}
},
onload() {
awaitingStyles.delete(state);
resolveStyle();

if (!awaitingStyles.size) {
foucFix.remove();
}
}
});

return linkEl;
}

default:
throw new Error(`Unknown type "${style.type}" for a style descriptor`);
throw new Error(`Unknown type "${(style as any).type}" for a style descriptor`);
}
}));

if (awaitingStyles.size) {
readyStyles = Promise.all(awaitingStyles);
el.append(foucFix);
await Promise.all(awaitingStyles);
foucFix.remove();
}
}

return readyStyles;
}
14 changes: 8 additions & 6 deletions src/core/utils/json.js → src/core/utils/json.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import jsonExt from '@discoveryjs/json-ext';

type Replacer = (key: string, value: any) => void;

export const {
stringifyInfo: jsonStringifyInfo
} = jsonExt;

function prettyFn(fn, ws, property) {
function prettyFn(fn: Function, ws: string, property: string) {
const src = String(fn);
const [prefix, name] = src.match(/^(?:\S+\s+)?(\S+)\(/) || [];

Expand All @@ -19,15 +21,15 @@ function prettyFn(fn, ws, property) {
}

const lines = src.split(/\n/);
const minOffset = lines[lines.length - 1].match(/^\s*/)[0].length;
const minOffset = lines[lines.length - 1].match(/^\s*/)?.[0].length || 0;
const stripOffset = new RegExp('^\\s{0,' + minOffset + '}');

return property + lines
.map((line, idx) => idx && line.length ? line.replace(stripOffset, ws) : line)
.join('\n');
}

function restoreValue(value, ws, property) {
function restoreValue(value: any, ws: string, property: string) {
if (typeof value === 'function') {
return prettyFn(value, ws, property);
}
Expand All @@ -46,9 +48,9 @@ const specialValueTypes = new Set([
'[object Date]'
]);

export function jsonStringifyAsJavaScript(value, replacer, space = 4) {
const specials = [];
const jsReplacer = function(key, value) {
export function jsonStringifyAsJavaScript(value: any, replacer: Replacer, space = 4) {
const specials: any[] = [];
const jsReplacer = function(key: string, value: any) {
if (typeof value === 'string' && toString.call(this[key]) === '[object Date]') {
value = this[key];
}
Expand Down
30 changes: 18 additions & 12 deletions src/core/utils/layout.js → src/core/utils/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,39 @@
const { documentElement } = document;
const standartsMode = document.compatMode === 'CSS1Compat';

export function getOffsetParent(node) {
let offsetParent = node.offsetParent;
export function getOffsetParent(node: HTMLElement) {
let offsetParent = node.offsetParent as HTMLElement;

while (
offsetParent !== null &&
offsetParent !== documentElement &&
getComputedStyle(offsetParent).position === 'static'
) {
offsetParent = offsetParent.offsetParent;
offsetParent = offsetParent.offsetParent as HTMLElement;
}

return offsetParent || documentElement;
}

export function getOverflowParent(node) {
let overflowParent = node.parentNode;
export function getOverflowParent(node: HTMLElement) {
let overflowParent = node.parentNode as HTMLElement;

while (
overflowParent !== null &&
overflowParent !== documentElement &&
getComputedStyle(overflowParent).overflow === 'visible'
) {
overflowParent = overflowParent.parentNode;
overflowParent = overflowParent.parentNode as HTMLElement;
}

return overflowParent || documentElement;
}

export function getPageOffset(element) {
export function getPageOffset(element: HTMLElement | null = null) {
let top = 0;
let left = 0;

if (element && element.getBoundingClientRect) {
if (typeof element?.getBoundingClientRect === 'function') {
// offset relative to element
const rect = element.getBoundingClientRect();

Expand Down Expand Up @@ -62,14 +62,17 @@ export function getPageOffset(element) {
};
}

export function getBoundingRect(element, relElement) {
export function getBoundingRect(
element: HTMLElement | Window,
relElement: HTMLElement | null
) {
const offset = getPageOffset(relElement);
let top = 0;
let left = 0;
let right = 0;
let bottom = 0;

if (element && element.getBoundingClientRect) {
if (element instanceof HTMLElement && typeof element?.getBoundingClientRect === 'function') {
({ top, left, right, bottom } = element.getBoundingClientRect());
}

Expand All @@ -83,15 +86,18 @@ export function getBoundingRect(element, relElement) {
};
}

export function getViewportRect(element, relElement) {
export function getViewportRect(
element: HTMLElement | Window,
relElement: HTMLElement | null = null
) {
const topViewport = standartsMode ? document.documentElement : document.body;
let { top, left } = element === topViewport && !relElement
? getPageOffset()
: getBoundingRect(element, relElement);
let width;
let height;

if (!element || element === window) {
if (!element || element instanceof Window) {
width = window.innerWidth || 0;
height = window.innerHeight || 0;
} else {
Expand Down
File renamed without changes.
Loading

0 comments on commit c6f6b2d

Please sign in to comment.