feat(styles): share accent cookies across app domains
This commit is contained in:
1 parent
32187a425c
commit
6133d33b8c
5 files changed
+50
-11
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/cli",
|
"name": "@wrnexus/cli",
|
||||||
"version": "0.8.25",
|
"version": "0.8.26",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/dev-server",
|
"name": "@wrnexus/dev-server",
|
||||||
"version": "0.8.24",
|
"version": "0.8.25",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/styles",
|
"name": "@wrnexus/styles",
|
||||||
"version": "0.8.10",
|
"version": "0.8.11",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -98,6 +98,19 @@ export interface ThemeAccentConfig {
|
|||||||
default?: ThemePaletteName | false;
|
default?: ThemePaletteName | false;
|
||||||
/** Runtime-selectable accent names. Defaults to every built-in THEME_PALETTE. */
|
/** Runtime-selectable accent names. Defaults to every built-in THEME_PALETTE. */
|
||||||
options?: ThemePaletteName[];
|
options?: ThemePaletteName[];
|
||||||
|
/** Cookie scope used to persist the selected accent across related applications. */
|
||||||
|
cookie?: ThemeCookieConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ThemeCookieConfig {
|
||||||
|
/** Parent domain shared by related applications, for example `localhost` or `.example.com`. */
|
||||||
|
domain?: string;
|
||||||
|
/** Cookie path. Defaults to `/`. */
|
||||||
|
path?: string;
|
||||||
|
/** SameSite policy. Defaults to `Lax`. */
|
||||||
|
sameSite?: "Strict" | "Lax" | "None";
|
||||||
|
/** Force or disable Secure. When omitted it follows the current protocol. */
|
||||||
|
secure?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ThemeConfig {
|
export interface ThemeConfig {
|
||||||
@@ -117,6 +130,7 @@ export interface ResolvedTheme {
|
|||||||
themes: Record<string, ThemeTokens>;
|
themes: Record<string, ThemeTokens>;
|
||||||
defaultAccent?: ThemePaletteName;
|
defaultAccent?: ThemePaletteName;
|
||||||
accentNames: ThemePaletteName[];
|
accentNames: ThemePaletteName[];
|
||||||
|
accentCookie?: ThemeCookieConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cookies used by the SSR renderer and client runtime. */
|
/** Cookies used by the SSR renderer and client runtime. */
|
||||||
@@ -494,6 +508,7 @@ export function resolveThemeConfig(config?: ThemeConfig): ResolvedTheme {
|
|||||||
themes,
|
themes,
|
||||||
defaultAccent,
|
defaultAccent,
|
||||||
accentNames,
|
accentNames,
|
||||||
|
accentCookie: config?.accent?.cookie,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,6 +637,7 @@ export function renderThemeRuntime(theme: ResolvedTheme): string {
|
|||||||
const accentNames = JSON.stringify(theme.accentNames);
|
const accentNames = JSON.stringify(theme.accentNames);
|
||||||
const defaultAccent = JSON.stringify(theme.defaultAccent ?? null);
|
const defaultAccent = JSON.stringify(theme.defaultAccent ?? null);
|
||||||
const cssPrefix = JSON.stringify(THEME_CSS_PREFIX);
|
const cssPrefix = JSON.stringify(THEME_CSS_PREFIX);
|
||||||
|
const accentCookie = JSON.stringify(theme.accentCookie ?? {});
|
||||||
|
|
||||||
return `(function(){
|
return `(function(){
|
||||||
var THEME_COOKIE=${JSON.stringify(THEME_COOKIE)};
|
var THEME_COOKIE=${JSON.stringify(THEME_COOKIE)};
|
||||||
@@ -631,6 +647,7 @@ export function renderThemeRuntime(theme: ResolvedTheme): string {
|
|||||||
var DEFAULT_THEME=${JSON.stringify(theme.default)};
|
var DEFAULT_THEME=${JSON.stringify(theme.default)};
|
||||||
var DEFAULT_ACCENT=${defaultAccent};
|
var DEFAULT_ACCENT=${defaultAccent};
|
||||||
var THEME_CSS_PREFIX=${cssPrefix};
|
var THEME_CSS_PREFIX=${cssPrefix};
|
||||||
|
var ACCENT_COOKIE_OPTIONS=${accentCookie};
|
||||||
var MAX_AGE=31536000;
|
var MAX_AGE=31536000;
|
||||||
var el=document.documentElement;
|
var el=document.documentElement;
|
||||||
|
|
||||||
@@ -646,15 +663,22 @@ export function renderThemeRuntime(theme: ResolvedTheme): string {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeCookie(name,value){
|
function cookieOptions(options){
|
||||||
var secure=location.protocol==="https:"?";secure":"";
|
options=options||{};
|
||||||
document.cookie=encodeURIComponent(name)+"="+encodeURIComponent(value)+
|
var path=options.path||"/";
|
||||||
";path=/;max-age="+MAX_AGE+";samesite=lax"+secure;
|
var sameSite=options.sameSite||"Lax";
|
||||||
|
var domain=options.domain?";domain="+options.domain:"";
|
||||||
|
var secure=options.secure===true||(options.secure!==false&&location.protocol==="https:")?";secure":"";
|
||||||
|
return ";path="+path+";samesite="+sameSite+domain+secure;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteCookie(name){
|
function writeCookie(name,value,options){
|
||||||
var secure=location.protocol==="https:"?";secure":"";
|
document.cookie=encodeURIComponent(name)+"="+encodeURIComponent(value)+
|
||||||
document.cookie=encodeURIComponent(name)+"=;path=/;max-age=0;samesite=lax"+secure;
|
";max-age="+MAX_AGE+cookieOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCookie(name,options){
|
||||||
|
document.cookie=encodeURIComponent(name)+"=;max-age=0"+cookieOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncStylesheet(themeName,accentName){
|
function syncStylesheet(themeName,accentName){
|
||||||
@@ -705,7 +729,8 @@ export function renderThemeRuntime(theme: ResolvedTheme): string {
|
|||||||
function setAccent(name){
|
function setAccent(name){
|
||||||
if(ACCENTS.indexOf(name)<0)return;
|
if(ACCENTS.indexOf(name)<0)return;
|
||||||
el.setAttribute("data-accent",name);
|
el.setAttribute("data-accent",name);
|
||||||
writeCookie(ACCENT_COOKIE,name);
|
deleteCookie(ACCENT_COOKIE);
|
||||||
|
writeCookie(ACCENT_COOKIE,name,ACCENT_COOKIE_OPTIONS);
|
||||||
syncStylesheet(getTheme(),name);
|
syncStylesheet(getTheme(),name);
|
||||||
syncAccentButtons(document);
|
syncAccentButtons(document);
|
||||||
window.dispatchEvent(new CustomEvent("wrnexus:accent-changed",{detail:{accent:name}}));
|
window.dispatchEvent(new CustomEvent("wrnexus:accent-changed",{detail:{accent:name}}));
|
||||||
@@ -714,6 +739,7 @@ export function renderThemeRuntime(theme: ResolvedTheme): string {
|
|||||||
function clearAccent(){
|
function clearAccent(){
|
||||||
el.removeAttribute("data-accent");
|
el.removeAttribute("data-accent");
|
||||||
deleteCookie(ACCENT_COOKIE);
|
deleteCookie(ACCENT_COOKIE);
|
||||||
|
deleteCookie(ACCENT_COOKIE,ACCENT_COOKIE_OPTIONS);
|
||||||
syncStylesheet(getTheme(),null);
|
syncStylesheet(getTheme(),null);
|
||||||
syncAccentButtons(document);
|
syncAccentButtons(document);
|
||||||
window.dispatchEvent(new CustomEvent("wrnexus:accent-changed",{detail:{accent:null}}));
|
window.dispatchEvent(new CustomEvent("wrnexus:accent-changed",{detail:{accent:null}}));
|
||||||
|
|||||||
@@ -46,6 +46,19 @@ describe("theme accents", () => {
|
|||||||
expect(runtime).not.toContain("localStorage");
|
expect(runtime).not.toContain("localStorage");
|
||||||
expect(runtime).not.toContain("style.setProperty");
|
expect(runtime).not.toContain("style.setProperty");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("runtime scopes accent cookies to a configured parent domain", () => {
|
||||||
|
const theme = resolveThemeConfig({
|
||||||
|
palette: "rose",
|
||||||
|
accent: { cookie: { domain: "localhost", path: "/", sameSite: "Lax", secure: false } },
|
||||||
|
});
|
||||||
|
const runtime = renderThemeRuntime(theme);
|
||||||
|
|
||||||
|
expect(theme.accentCookie?.domain).toBe("localhost");
|
||||||
|
expect(runtime).toContain('"domain":"localhost"');
|
||||||
|
expect(runtime).toContain("deleteCookie(ACCENT_COOKIE);");
|
||||||
|
expect(runtime).toContain("writeCookie(ACCENT_COOKIE,name,ACCENT_COOKIE_OPTIONS)");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
test("live document accent has priority over cookie and default", () => {
|
test("live document accent has priority over cookie and default", () => {
|
||||||
const runtime = renderThemeRuntime(
|
const runtime = renderThemeRuntime(
|
||||||
|
|||||||
Reference in new issue
Block a user