Skip to content

Commit 98ffa49

Browse files
committed
fix: fix dark modes and chevron icon for sidebar
1 parent 1c01820 commit 98ffa49

10 files changed

Lines changed: 2087 additions & 2044 deletions

File tree

packages/xyd-core/src/types/settings.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ export interface Appearance {
176176
*/
177177
colorScheme?: "light" | "dark" | "os" | false
178178

179+
/**
180+
* If `false` then the color scheme button will not be displayed.
181+
*/
182+
colorSchemeButton?: false
183+
179184
/**
180185
* Colors configuration for the theme.
181186
*/

packages/xyd-framework/packages/react/components/FwNav.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import { useActivePage, useDefaultHeaderItems } from "../hooks";
1111
import { Surface } from "./Surfaces";
1212
import { FwLogo } from "./FwLogo";
1313
import { FwHeaderItem, FwHeaderItems } from "./FwHeaderItems";
14-
import { useAppearance } from "../contexts";
14+
import { useAppearance, useShowColorSchemeButton } from "../contexts";
1515
import { WebEditorComponent } from "./WebEditorComponent";
1616

1717
// TODO: renamte to FwHeader ?
1818
export function FwNav() {
1919
const activeHeaderPage = useActivePage()
2020
const appearance = useAppearance()
21+
const showColorSchemeButton = useShowColorSchemeButton()
2122

2223
const Header = FwHeaderItems()
2324
const logo = appearance?.logo?.header ? <WebEditorComponent.NavItemRaw
@@ -56,7 +57,7 @@ export function FwNav() {
5657
</>
5758
}
5859
floatRightSurface={<>
59-
{appearance?.colorScheme !== false && <Nav.ItemRaw>
60+
{showColorSchemeButton && <Nav.ItemRaw>
6061
<ColorSchemeButton />
6162
</Nav.ItemRaw>}
6263
<Nav.ItemRaw>

packages/xyd-framework/packages/react/contexts/framework.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export function Framework(props: FrameworkProps) {
8181
interface FrameworkPageProps {
8282
children: React.ReactNode
8383

84-
ContentComponent?: (props: { components: any, children: React.ReactNode }) => React.JSX.Element
84+
ContentComponent?: (props: { components: any, children: React.ReactNode }) => React.JSX.Element
8585
ContentOriginal?: (props: { components: any, children: React.ReactNode }) => React.JSX.Element
8686

8787
metadata: Metadata
@@ -210,4 +210,13 @@ export function useBannerContent() {
210210
const ctx = useContext(FrameworkContext)
211211

212212
return ctx.BannerContent
213+
}
214+
215+
export function useShowColorSchemeButton() {
216+
const ctx = useContext(FrameworkContext)
217+
218+
const showColorSchemeButton = ctx.settings.theme?.appearance?.colorScheme !== false
219+
&& ctx.settings.theme?.appearance?.colorSchemeButton !== false
220+
221+
return showColorSchemeButton
213222
}

packages/xyd-framework/packages/react/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export {
1919
useAppearance,
2020
useContentOriginal,
2121
useEditLink,
22+
useShowColorSchemeButton,
2223
} from "./contexts"
2324

2425
export {

packages/xyd-host/app/root.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,17 @@ function DefaultMetas() {
135135
}
136136

137137
function ColorSchemeScript() {
138+
const defaultColorScheme = settings?.theme?.appearance?.colorScheme || 'os'
139+
140+
// Inject settings into the script
141+
const scriptWithSettings = `
142+
window.__xydColorSchemeSettings = ${JSON.stringify({ defaultColorScheme })};
143+
${colorSchemeScript}
144+
`;
145+
138146
return <script
139147
dangerouslySetInnerHTML={{
140-
__html: colorSchemeScript
148+
__html: scriptWithSettings
141149
}}
142150
/>
143151
}

packages/xyd-host/app/scripts/colorSchemeScript.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
(function () {
22
try {
3-
var theme = localStorage.getItem('xyd-color-scheme') || 'auto';
3+
// Get settings from window object (injected by the server)
4+
const settings = (window).__xydColorSchemeSettings;
5+
const defaultColorScheme = settings.defaultColorScheme;
6+
7+
var theme = localStorage.getItem('xyd-color-scheme') || defaultColorScheme || 'auto';
48

59
if (theme === 'auto') {
610
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
711

812
if (isDark) {
913
document.documentElement.setAttribute('data-color-scheme', 'dark');
14+
} else {
15+
document.documentElement.setAttribute('data-color-scheme', 'light');
1016
}
1117
} else if (theme === 'light') {
1218
document.documentElement.setAttribute('data-color-scheme', 'light');
@@ -17,6 +23,8 @@
1723
// Fallback to system preference if localStorage fails
1824
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
1925
document.documentElement.setAttribute('data-color-scheme', 'dark');
26+
} else {
27+
document.documentElement.setAttribute('data-color-scheme', 'light');
2028
}
2129
}
2230
})();

packages/xyd-theme-gusto/src/theme.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {css} from "@linaria/core";
55
import {ColorSchemeButton} from "@xyd-js/components/writer";
66
import {UISidebar} from "@xyd-js/ui";
77
import {BaseTheme} from "@xyd-js/themes"
8-
import {FwLogo, useSettings} from "@xyd-js/framework/react";
8+
import {FwLogo, useShowColorSchemeButton} from "@xyd-js/framework/react";
99
import syntaxThemeClassic from "@xyd-js/components/coder/themes/classic.js"
1010

1111
import "./imports.css"
@@ -52,14 +52,14 @@ export default class ThemeGusto extends BaseTheme {
5252
}
5353

5454
function _SidebarTop() {
55-
const settings = useSettings()
56-
55+
const showColorSchemeButton = useShowColorSchemeButton()
56+
5757
return <>
5858
<UISidebar.Item ghost>
5959
<div className={styles.SidebarTop}>
6060
<FwLogo/>
6161
<div>
62-
{settings?.theme?.appearance?.colorScheme !== false && <ColorSchemeButton/>}
62+
{showColorSchemeButton && <ColorSchemeButton/>}
6363
</div>
6464
</div>
6565
</UISidebar.Item>

packages/xyd-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"autoprefixer": "^10.4.20",
5050
"postcss": "^8.4.47",
5151
"postcss-import": "^16.1.0",
52+
"postcss-url": "^10.1.3",
5253
"rimraf": "^3.0.2",
5354
"rollup": "^4.27.4",
5455
"rollup-plugin-css-only": "^4.5.2",

packages/xyd-ui/rollup.config.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import {fileURLToPath} from 'url';
4+
15
import resolve from '@rollup/plugin-node-resolve';
26
import commonjs from '@rollup/plugin-commonjs';
37
import typescript from '@rollup/plugin-typescript';
48
import dts from 'rollup-plugin-dts';
59
import {terser} from 'rollup-plugin-terser';
610
import babel from '@rollup/plugin-babel';
711
import postcss from 'rollup-plugin-postcss';
12+
import postcssInlineUrl from 'postcss-url';
813
import wyw from '@wyw-in-js/rollup';
14+
import postcssProcessor from 'postcss';
915

1016
import {createRequire} from 'module';
1117

@@ -22,6 +28,9 @@ const external = [
2228
...Object.keys(devDependencies),
2329
];
2430

31+
const __filename = fileURLToPath(import.meta.url);
32+
const __dirname = path.dirname(__filename);
33+
2534
export default [
2635
{
2736
input: {
@@ -83,6 +92,63 @@ export default [
8392
],
8493
}),
8594
terser(),
95+
{
96+
name: 'postcss-svg-inline',
97+
async writeBundle() {
98+
const cssFile = path.resolve(__dirname, 'dist/index.css');
99+
100+
if (!fs.existsSync(cssFile)) return;
101+
const css = fs.readFileSync(cssFile, 'utf8');
102+
103+
// Custom PostCSS plugin to handle CSS custom properties with local file paths
104+
const customInlinePlugin = {
105+
postcssPlugin: 'custom-inline-svg',
106+
async Declaration(decl) {
107+
if (decl.value && decl.value.includes('url(')) {
108+
// Find all url() patterns in the declaration value
109+
const urlRegex = /url\(([^)]+)\)/g;
110+
let match;
111+
let newValue = decl.value;
112+
113+
while ((match = urlRegex.exec(decl.value)) !== null) {
114+
const urlPath = match[1].replace(/['"]/g, '');
115+
116+
// Check if it's a local file path
117+
if (urlPath.startsWith('/') && urlPath.includes('.svg')) {
118+
try {
119+
const fullPath = path.resolve(__dirname, urlPath);
120+
if (fs.existsSync(fullPath)) {
121+
const svgContent = fs.readFileSync(fullPath, 'utf8');
122+
const encodedSvg = encodeURIComponent(svgContent);
123+
const dataUrl = `data:image/svg+xml,${encodedSvg}`;
124+
125+
// Replace the url() with the data URL
126+
newValue = newValue.replace(match[0], `url("${dataUrl}")`);
127+
}
128+
} catch (error) {
129+
console.warn(`Failed to inline SVG: ${urlPath}`, error.message);
130+
}
131+
}
132+
}
133+
134+
decl.value = newValue;
135+
}
136+
}
137+
};
138+
139+
const result = await postcssProcessor([
140+
customInlinePlugin,
141+
postcssInlineUrl({
142+
url: 'inline',
143+
encodeType: 'encodeURIComponent',
144+
maxSize: Infinity,
145+
filter: '**/*.svg',
146+
})
147+
]).process(css, { from: cssFile, to: cssFile });
148+
149+
fs.writeFileSync(cssFile, result.css);
150+
}
151+
},
86152
],
87153
external
88154
},

0 commit comments

Comments
 (0)