Skip to content

Commit e4bd153

Browse files
authored
chore(qwik-themes): move code internally + signal implementation (#922)
* chore(qwik-themes): move code internally under _state folder * chore(themes): linting * fix(themes): use signals instead of stores * chore(themes provider): useVisibleTask to useOnWindow * chore(themes): move to qwik-ui/themes
1 parent b446009 commit e4bd153

23 files changed

+642
-27
lines changed

apps/website/src/components/copy-css-config/copy-css-config.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Modal } from '@qwik-ui/headless';
33
import { Button } from '~/components/ui';
44
import { extractThemeCSS } from '@qwik-ui/utils';
55
import { LuX } from '@qwikest/icons/lucide';
6-
import { useTheme } from 'qwik-themes';
6+
import { useTheme } from '@qwik-ui/themes';
77
import globalCSS from '~/global.css?raw';
88
import { Highlight } from '../highlight/highlight';
99

@@ -12,16 +12,16 @@ export default component$(() => {
1212

1313
const cssThemeOutput = useSignal<string>('');
1414

15-
const { theme } = useTheme();
15+
const { themeSig } = useTheme();
1616

1717
return (
1818
<Modal.Root bind:show={showSig}>
1919
<Button
2020
onClick$={() => {
2121
cssThemeOutput.value = extractThemeCSS(
22-
theme === 'dark' || theme === 'light'
22+
themeSig.value === 'dark' || themeSig.value === 'light'
2323
? 'border-radius-0 simple primary-cyan-600 light base-slate'
24-
: theme,
24+
: themeSig.value,
2525
globalCSS,
2626
);
2727
showSig.value = true;

apps/website/src/components/header/header.tsx

+8-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import { MoonIcon } from '../icons/MoonIcon';
2020
import { SunIcon } from '../icons/SunIcon';
2121
import { LogoIcon, LogoWithBorders } from '../icons/logo';
2222

23-
import { useTheme } from 'qwik-themes';
23+
import { useTheme } from '@qwik-ui/themes';
24+
2425
import { Modal } from '@qwik-ui/headless';
2526
import { useAppState } from '~/_state/use-app-state';
2627
import { LuX } from '@qwikest/icons/lucide';
@@ -126,13 +127,13 @@ export default component$(({ showVersion = false }: HeaderProps) => {
126127
}
127128
});
128129

129-
const { theme } = useTheme();
130+
const { themeSig } = useTheme();
130131

131132
return (
132133
<Modal.Root
133134
class={cn(
134135
'sticky top-0 z-10 flex h-16 justify-center border-b bg-background',
135-
theme?.includes('brutalist') && 'border-b-2',
136+
themeSig.value?.includes('brutalist') && 'border-b-2',
136137
)}
137138
bind:show={isSidebarOpenedSig}
138139
>
@@ -219,7 +220,7 @@ export default component$(({ showVersion = false }: HeaderProps) => {
219220
});
220221

221222
const DarkModeToggle = component$<PropsOf<typeof Button>>(({ ...props }) => {
222-
const { theme, setTheme } = useTheme();
223+
const { themeSig } = useTheme();
223224
const switchLightDark = $((input: string | string[]): string | string[] | undefined => {
224225
const switchWord = (word: string): string =>
225226
word.includes('light')
@@ -237,7 +238,9 @@ const DarkModeToggle = component$<PropsOf<typeof Button>>(({ ...props }) => {
237238
aria-label="Toggle dark mode"
238239
size="icon"
239240
look="ghost"
240-
onClick$={async () => setTheme(await switchLightDark(theme || 'light'))}
241+
onClick$={async () =>
242+
(themeSig.value = await switchLightDark(themeSig.value || 'light'))
243+
}
241244
>
242245
<div class="hidden dark:block">
243246
<MoonIcon />

apps/website/src/components/icons/logo.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { PropsOf, component$ } from '@builder.io/qwik';
22
import { cn } from '@qwik-ui/utils';
3-
import { useTheme } from 'qwik-themes';
3+
import { useTheme } from '@qwik-ui/themes';
44

55
export const Logo = component$<PropsOf<'svg'>>(({ ...props }) => {
66
return (
@@ -57,7 +57,7 @@ export const Logo = component$<PropsOf<'svg'>>(({ ...props }) => {
5757
});
5858

5959
export const LogoWithBorders = component$<PropsOf<'svg'>>(({ ...props }) => {
60-
const { theme } = useTheme();
60+
const { themeSig } = useTheme();
6161
return (
6262
<svg
6363
{...props}
@@ -72,7 +72,7 @@ export const LogoWithBorders = component$<PropsOf<'svg'>>(({ ...props }) => {
7272
clip-rule="evenodd"
7373
>
7474
<g
75-
class={cn(theme?.includes('light') ? 'stroke-black' : 'stroke-white')}
75+
class={cn(themeSig.value?.includes('light') ? 'stroke-black' : 'stroke-white')}
7676
stroke-width="8"
7777
fill="none"
7878
>
@@ -118,7 +118,7 @@ export const LogoWithBorders = component$<PropsOf<'svg'>>(({ ...props }) => {
118118
});
119119

120120
export const LogoIcon = component$<PropsOf<'svg'>>(({ ...props }) => {
121-
const { theme } = useTheme();
121+
const { themeSig } = useTheme();
122122
return (
123123
<svg
124124
{...props}
@@ -129,7 +129,7 @@ export const LogoIcon = component$<PropsOf<'svg'>>(({ ...props }) => {
129129
xmlns="http://www.w3.org/2000/svg"
130130
>
131131
<g
132-
class={cn(theme?.includes('light') ? 'stroke-black' : 'stroke-white')}
132+
class={cn(themeSig.value?.includes('light') ? 'stroke-black' : 'stroke-white')}
133133
stroke-width="8"
134134
fill="none"
135135
>

apps/website/src/components/make-it-yours/make-it-yours.tsx

+17-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
cn,
1111
} from '@qwik-ui/utils';
1212
import { LuSlidersHorizontal, LuX } from '@qwikest/icons/lucide';
13-
import { useTheme } from 'qwik-themes';
13+
import { useTheme } from '@qwik-ui/themes';
14+
1415
import { Button, Modal, buttonVariants } from '~/components/ui';
1516

1617
import { useAppState } from '~/_state/use-app-state';
@@ -19,10 +20,10 @@ import CopyCssConfig from '../copy-css-config/copy-css-config';
1920
export default component$<PropsOf<typeof Button>>(() => {
2021
const rootStore = useAppState();
2122

22-
const { theme, setTheme } = useTheme();
23+
const { themeSig } = useTheme();
2324

2425
const themeComputedObjectSig = useComputed$((): ThemeConfig => {
25-
if (!theme || theme === 'light') {
26+
if (!themeSig.value || themeSig.value === 'light') {
2627
return {
2728
font: ThemeFonts.SANS,
2829
mode: ThemeModes.LIGHT,
@@ -33,7 +34,7 @@ export default component$<PropsOf<typeof Button>>(() => {
3334
};
3435
}
3536

36-
if (theme === 'dark') {
37+
if (themeSig.value === 'dark') {
3738
return {
3839
font: ThemeFonts.SANS,
3940
mode: ThemeModes.DARK,
@@ -44,7 +45,9 @@ export default component$<PropsOf<typeof Button>>(() => {
4445
};
4546
}
4647

47-
const themeArray = Array.isArray(theme) ? theme : theme.split(' ');
48+
const themeArray = Array.isArray(themeSig.value)
49+
? themeSig.value
50+
: themeSig.value.split(' ');
4851
return {
4952
font: themeArray[0],
5053
mode: themeArray[1],
@@ -91,7 +94,7 @@ export default component$<PropsOf<typeof Button>>(() => {
9194
themeComputedObjectSig.value.font = ThemeFonts.SANS;
9295
}
9396
themeComputedObjectSig.value.style = el.value;
94-
setTheme(await themeStoreToThemeClasses$());
97+
themeSig.value = await themeStoreToThemeClasses$();
9598
}}
9699
>
97100
<option value={'simple'}>Simple</option>
@@ -114,7 +117,7 @@ export default component$<PropsOf<typeof Button>>(() => {
114117
onClick$={async () => {
115118
themeComputedObjectSig.value.baseColor = baseColor;
116119

117-
setTheme(await themeStoreToThemeClasses$());
120+
themeSig.value = await themeStoreToThemeClasses$();
118121
}}
119122
class={cn(
120123
'flex h-3 w-3 items-center justify-center rounded-none',
@@ -165,7 +168,7 @@ export default component$<PropsOf<typeof Button>>(() => {
165168
size="icon"
166169
onClick$={async () => {
167170
themeComputedObjectSig.value.primaryColor = primaryColor;
168-
setTheme(await themeStoreToThemeClasses$());
171+
themeSig.value = await themeStoreToThemeClasses$();
169172
}}
170173
class={cn(
171174
'h-3 w-3 rounded-none',
@@ -182,7 +185,7 @@ export default component$<PropsOf<typeof Button>>(() => {
182185
primaryColor === 'primary-zinc-900' ||
183186
primaryColor === 'primary-neutral-900' ||
184187
primaryColor === 'primary-stone-900') &&
185-
theme?.includes('dark') ? (
188+
themeSig.value?.includes('dark') ? (
186189
<span
187190
class={cn(
188191
'flex h-[10px] w-[10px] shrink-0 rounded-none',
@@ -439,7 +442,7 @@ export default component$<PropsOf<typeof Button>>(() => {
439442
look="outline"
440443
onClick$={async () => {
441444
themeComputedObjectSig.value.borderRadius = borderRadius;
442-
setTheme(await themeStoreToThemeClasses$());
445+
themeSig.value = await themeStoreToThemeClasses$();
443446
}}
444447
class={cn('w-12', isActive && 'mb-2 border-ring')}
445448
>
@@ -462,7 +465,7 @@ export default component$<PropsOf<typeof Button>>(() => {
462465
themeComputedObjectSig.value.mode =
463466
themeComputedObjectSig.value.mode?.includes('light') ? 'dark' : 'light';
464467

465-
setTheme(await themeStoreToThemeClasses$());
468+
themeSig.value = await themeStoreToThemeClasses$();
466469
}}
467470
/>
468471
</div>
@@ -471,7 +474,9 @@ export default component$<PropsOf<typeof Button>>(() => {
471474
<footer class=" flex w-full justify-between gap-4">
472475
<Button
473476
look="ghost"
474-
onClick$={() => setTheme(theme?.includes('dark') ? 'dark' : 'light')}
477+
onClick$={() => {
478+
themeSig.value = themeSig.value?.includes('dark') ? 'dark' : 'light';
479+
}}
475480
>
476481
Reset
477482
</Button>

apps/website/src/root.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { AppState } from './_state/app-state.type';
1010
import { RouterHead } from './components/router-head/router-head';
1111
import globalStyles from './global.css?inline';
1212

13-
import { ThemeProvider } from 'qwik-themes';
13+
import { ThemeProvider } from '@qwik-ui/themes';
1414

1515
import '@fontsource-variable/inter';
1616
import {

packages/themes/.eslintrc.json

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"extends": [
3+
"../../.eslintrc.json",
4+
"eslint:recommended",
5+
"plugin:@typescript-eslint/recommended",
6+
"plugin:qwik/recommended"
7+
],
8+
"parser": "@typescript-eslint/parser",
9+
"parserOptions": {
10+
"project": ["packages/themes/tsconfig.*?.json"],
11+
"ecmaVersion": 2021,
12+
"sourceType": "module",
13+
"ecmaFeatures": {
14+
"jsx": true
15+
}
16+
},
17+
"plugins": ["@typescript-eslint"],
18+
"ignorePatterns": ["!**/*", "**/.storybook/*"],
19+
"overrides": [
20+
{
21+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
22+
"rules": {}
23+
},
24+
{
25+
"files": ["*.ts", "*.tsx"],
26+
"rules": {}
27+
},
28+
{
29+
"files": ["*.js", "*.jsx"],
30+
"rules": {}
31+
}
32+
]
33+
}

packages/themes/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Changelog

packages/themes/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 HiRez.io, Qwikifiers
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

packages/themes/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Qwik UI themes
2+
3+
## useTheme hook
4+
5+
Provides a way to access/set a theme signal to setup 'dark'/'light'/'system' or a combination of custom themes (e.g. 'dark green radius-100') programmatically.
6+
7+
## ThemeProvider
8+
9+
To set up options for the themes.
10+
11+
# License
12+
13+
MIT

packages/themes/package.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "@qwik-ui/themes",
3+
"version": "0.0.1",
4+
"description": "Qwik UI themes",
5+
"publishConfig": {
6+
"access": "public"
7+
},
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/qwikifiers/qwik-ui",
11+
"directory": "packages/themes"
12+
},
13+
"main": "./index.qwik.cjs",
14+
"qwik": "./index.qwik.mjs",
15+
"module": "./index.qwik.mjs",
16+
"types": "./index.d.ts",
17+
"type": "module",
18+
"engines": {
19+
"node": ">=16.0.0"
20+
},
21+
"private": false,
22+
"scripts": {},
23+
"peerDependencies": {
24+
"@builder.io/qwik": "1.7.2"
25+
},
26+
"devDependencies": {
27+
}
28+
}

packages/themes/project.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "themes",
3+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "packages/themes/src",
5+
"projectType": "library",
6+
"targets": {
7+
"build": {
8+
"executor": "@nx/js:tsc",
9+
"outputs": ["{options.outputPath}"],
10+
"options": {
11+
"main": "src/index.ts",
12+
"outputPath": "dist/packages/themes",
13+
"tsConfig": "packages/themes/tsconfig.lib.json"
14+
}
15+
},
16+
"lint": {
17+
"executor": "@nx/eslint:lint",
18+
"outputs": ["{options.outputFile}"],
19+
"options": {
20+
"lintFilePatterns": ["packages/themes/**/*.{ts,tsx,js,jsx}"]
21+
}
22+
}
23+
},
24+
"tags": []
25+
}

packages/themes/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type { Theme } from './lib/types';
2+
export { useTheme, ThemeProvider } from './lib/provider';

0 commit comments

Comments
 (0)