From 170fded0481420873041abc873eeca44229259dc Mon Sep 17 00:00:00 2001 From: SegaraRai Date: Mon, 17 Jun 2024 03:44:14 +0900 Subject: [PATCH] feat: initial RTL support --- src/server/jsx/render.ts | 8 ++- src/widget/WeatherWidget.tsx | 126 +++++++++++++++++++++++++---------- src/widget/animations.css | 18 +++++ 3 files changed, 112 insertions(+), 40 deletions(-) diff --git a/src/server/jsx/render.ts b/src/server/jsx/render.ts index 568bb00..366cd26 100644 --- a/src/server/jsx/render.ts +++ b/src/server/jsx/render.ts @@ -64,7 +64,7 @@ function escapeHTMLComment(value: string): string { .replaceAll("--", "--"); } -function renderProps(props: Readonly>): string { +function renderAttributes(props: Readonly>): string { return Object.entries(props) .filter( ([key, value]) => @@ -141,7 +141,9 @@ function isNoNewlineTag(tag: string): boolean { export function render(node: Node, noNewlines = false): string { switch (node[SYMBOL_NODE_TYPE]) { case NODE_TYPE_SUBSTANTIAL: { - const { t: tag, p: props, c: children } = node; + const { t: tag, p: props } = node; + const children = node.p.children ? [node.p.children].flat() : node.c; + if (typeof tag === "string") { if (!isValidTagProp(tag)) { if (import.meta.env.DEV) { @@ -155,7 +157,7 @@ export function render(node: Node, noNewlines = false): string { return ``; } - const preamble = `<${tag}${renderProps(props)}`; + const preamble = `<${tag}${renderAttributes(props)}`; return children.some((child) => child != null && child !== "") ? `${preamble}>${renderChildren(children, noNewlines || isNoNewlineTag(tag))}` diff --git a/src/widget/WeatherWidget.tsx b/src/widget/WeatherWidget.tsx index eebf01d..5732fe5 100644 --- a/src/widget/WeatherWidget.tsx +++ b/src/widget/WeatherWidget.tsx @@ -1,5 +1,6 @@ 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"; @@ -74,6 +75,35 @@ function OpacityAnimation({ ); } +function createLogicalComponent( + tagName: T, + boxWidth: number, + flip: boolean +) { + const Tag = tagName; + if (!flip) { + return (props: PreactJSX.SVGAttributes) => ( + // @ts-expect-error + + ); + } + + 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 ; + }; +} + function formatDateTime( datetime: string, language: string, @@ -100,6 +130,13 @@ function formatDateTime( ]; } +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, @@ -172,6 +209,12 @@ export function WeatherWidget({ getTranslation(null, key) ?? (import.meta.env.DEV ? key : ""); + const isRTL = isRTLLanguage(language); + const BOX_SIZE = 400; + const LRect = createLogicalComponent("rect", BOX_SIZE, isRTL); + const LText = createLogicalComponent("text", BOX_SIZE, isRTL); + const LUse = createLogicalComponent("use", BOX_SIZE, isRTL); + return ( - - {/* Weather icon */} - {/* Weather description */} - {t(`wmo_${weather.current.weather_code}`) ?? t("wmo_unknown")} - + {/* Temperature */} - + {t(`unit_${prefTemperature}`)} - + {/* Humidity, Precipitation, Wind and Air Pressure */} {/* Humidity */} - - - + {/* Precipitation */} - - - + {showPrecipitation && ( - - - + {/* Wind */} - - - + {/* Sea Level Pressure */} - - - + {/* Location */} - {locationLabel} - - + {timezone} - + {/* Time */} - + {formattedWeekday} - - + {formattedDateTime} - + {/* Credit. No localization needed. */} @@ -490,7 +542,7 @@ export function WeatherWidget({ referrerpolicy="no-referrer" target="_blank" > - Weather.svg - + - data by Open-Meteo - + diff --git a/src/widget/animations.css b/src/widget/animations.css index c5d997e..540a3c2 100644 --- a/src/widget/animations.css +++ b/src/widget/animations.css @@ -16,6 +16,15 @@ } } +@keyframes slide-in-left-rtl { + from { + transform: translateX(-50%); + } + to { + transform: translateX(0); + } +} + @keyframes slide-in-up { from { transform: translateY(85%); @@ -33,3 +42,12 @@ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); } } + +@keyframes reveal-to-right-rtl { + from { + clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%); + } + to { + clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); + } +}