Skip to content

Commit

Permalink
refactor: move utility functions to other files
Browse files Browse the repository at this point in the history
  • Loading branch information
SegaraRai committed Jun 16, 2024
1 parent 170fded commit d9dc3df
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 111 deletions.
47 changes: 47 additions & 0 deletions src/widget/OpacityAnimation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
export function OpacityAnimation({
index,
total,
partDuration,
switchDuration,
}: {
readonly index: number;
readonly total: number;
readonly partDuration: number;
readonly switchDuration: number;
}) {
if (total <= 1) {
return <></>;
}

const totalDuration = total * partDuration;
const showSwitchStart = index * partDuration;
const showSwitchFinish = showSwitchStart + switchDuration;
const hideSwitchStart = showSwitchStart + partDuration - switchDuration;
const hideSwitchFinish = showSwitchStart + partDuration;
const keyTimes = [
0,
showSwitchStart / totalDuration,
showSwitchFinish / totalDuration,
hideSwitchStart / totalDuration,
hideSwitchFinish / totalDuration,
1,
];
const values = [0, 0, 1, 1, 0, 0];
if (index === 0) {
values.shift();
keyTimes.shift();
} else if (index === total - 1) {
values.pop();
keyTimes.pop();
}

return (
<animate
attributeName="opacity"
values={values.join(";")}
keyTimes={keyTimes.join(";")}
dur={`${totalDuration}s`}
repeatCount="indefinite"
/>
);
}
114 changes: 3 additions & 111 deletions src/widget/WeatherWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { IconSymbolsDefs } from "iconSymbols.tsx";
import inlineUnoCSS from "inline.uno.css?inline";
import type { JSX as PreactJSX } from "preact";
import type { Weather } from "../types/weather";
import { WidgetBackgroundGradient } from "./BackgroundGradient";
import { HTMLComment } from "./HTMLComment";
import { OpacityAnimation } from "./OpacityAnimation";
import animationsCSS from "./animations.css?inline";
import commentBanner from "./bannerComment.txt?raw";
import {
Expand All @@ -17,126 +17,18 @@ import {
TEMPERATURE_CONVERSION_MAP,
TEMPERATURE_FRACTION_DIGITS_MAP,
} from "./conversion";
import { formatDateTime } from "./format";
import { getWeatherIcon } from "./icons";
import { resolvePreferences } from "./resolvePreferences";
import rootCSS from "./root.css?inline";
import { createLogicalComponent, isRTLLanguage } from "./rtl";
import type { PreferencesSchema } from "./schemas";
import { getTheme } from "./theme";
import { getTranslation, getTranslationLanguage } from "./translations";

const ANIMATION_PART_DURATION = 8;
const ANIMATION_SWITCH_DURATION = 0.4;

function OpacityAnimation({
index,
total,
partDuration,
switchDuration,
}: {
readonly index: number;
readonly total: number;
readonly partDuration: number;
readonly switchDuration: number;
}) {
if (total <= 1) {
return <></>;
}

const totalDuration = total * partDuration;
const showSwitchStart = index * partDuration;
const showSwitchFinish = showSwitchStart + switchDuration;
const hideSwitchStart = showSwitchStart + partDuration - switchDuration;
const hideSwitchFinish = showSwitchStart + partDuration;
const keyTimes = [
0,
showSwitchStart / totalDuration,
showSwitchFinish / totalDuration,
hideSwitchStart / totalDuration,
hideSwitchFinish / totalDuration,
1,
];
const values = [0, 0, 1, 1, 0, 0];
if (index === 0) {
values.shift();
keyTimes.shift();
} else if (index === total - 1) {
values.pop();
keyTimes.pop();
}

return (
<animate
attributeName="opacity"
values={values.join(";")}
keyTimes={keyTimes.join(";")}
dur={`${totalDuration}s`}
repeatCount="indefinite"
/>
);
}

function createLogicalComponent<T extends "rect" | "text" | "use">(
tagName: T,
boxWidth: number,
flip: boolean
) {
const Tag = tagName;
if (!flip) {
return (props: PreactJSX.SVGAttributes<SVGElementTagNameMap[T]>) => (
// @ts-expect-error
<Tag {...props} />
);
}

return ({ ...props }) => {
const x = boxWidth - Number(props.x ?? "0") - Number(props.width ?? "0");
const textAnchor =
tagName === "text" ? props["text-anchor"] ?? "start" : undefined;
const flippedTextAnchor =
textAnchor === "start"
? "end"
: textAnchor === "end"
? "start"
: textAnchor;

// @ts-expect-error
return <Tag {...props} x={x} text-anchor={flippedTextAnchor} />;
};
}

function formatDateTime(
datetime: string,
language: string,
timeFormat: Exclude<PreferencesSchema["time_format"], "auto">
): [weekday: string, dateTime: string] {
const date = new Date(datetime + "Z");
return [
date.toLocaleString(language, {
timeZone: "UTC",
weekday: "long",
}),
date.toLocaleString(language, {
timeZone: "UTC",
...(timeFormat === "native"
? { dateStyle: "medium", timeStyle: "short" }
: {
month: "short",
day: "numeric",
hour: timeFormat === "24h" ? "2-digit" : "numeric",
hour12: timeFormat === "12h",
minute: "2-digit",
}),
}),
];
}

function isRTLLanguage(language: string): boolean {
const locale = new Intl.Locale(language);
// @ts-expect-error
const textInfo = locale.getTextInfo?.() ?? locale.textInfo;
return textInfo?.direction === "rtl";
}

export function WeatherWidget({
weather,
locationLabel,
Expand Down
27 changes: 27 additions & 0 deletions src/widget/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { PreferencesSchema } from "./schemas";

export function formatDateTime(
datetime: string,
language: string,
timeFormat: Exclude<PreferencesSchema["time_format"], "auto">
): [weekday: string, dateTime: string] {
const date = new Date(datetime + "Z");
return [
date.toLocaleString(language, {
timeZone: "UTC",
weekday: "long",
}),
date.toLocaleString(language, {
timeZone: "UTC",
...(timeFormat === "native"
? { dateStyle: "medium", timeStyle: "short" }
: {
month: "short",
day: "numeric",
hour: timeFormat === "24h" ? "2-digit" : "numeric",
hour12: timeFormat === "12h",
minute: "2-digit",
}),
}),
];
}
37 changes: 37 additions & 0 deletions src/widget/rtl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { JSX as PreactJSX } from "preact";

export function isRTLLanguage(language: string): boolean {
const locale = new Intl.Locale(language);
// @ts-expect-error

Check failure on line 5 in src/widget/rtl.tsx

View workflow job for this annotation

GitHub Actions / deploy

Include a description after the "@ts-expect-error" directive to explain why the @ts-expect-error is necessary. The description must be 3 characters or longer
const textInfo = locale.getTextInfo?.() ?? locale.textInfo;
return textInfo?.direction === "rtl";
}

export function createLogicalComponent<T extends "rect" | "text" | "use">(
tagName: T,
boxWidth: number,
flip: boolean
) {
const Tag = tagName;
if (!flip) {
return (props: PreactJSX.SVGAttributes<SVGElementTagNameMap[T]>) => (
// @ts-expect-error

Check failure on line 18 in src/widget/rtl.tsx

View workflow job for this annotation

GitHub Actions / deploy

Include a description after the "@ts-expect-error" directive to explain why the @ts-expect-error is necessary. The description must be 3 characters or longer
<Tag {...props} />
);
}

return ({ ...props }) => {
const x = boxWidth - Number(props.x ?? "0") - Number(props.width ?? "0");
const textAnchor =
tagName === "text" ? props["text-anchor"] ?? "start" : undefined;
const flippedTextAnchor =
textAnchor === "start"
? "end"
: textAnchor === "end"
? "start"
: textAnchor;

// @ts-expect-error

Check failure on line 34 in src/widget/rtl.tsx

View workflow job for this annotation

GitHub Actions / deploy

Include a description after the "@ts-expect-error" directive to explain why the @ts-expect-error is necessary. The description must be 3 characters or longer
return <Tag {...props} x={x} text-anchor={flippedTextAnchor} />;
};
}

0 comments on commit d9dc3df

Please sign in to comment.