Skip to content

Commit a70b446

Browse files
committed
ntp: customizer drawer
1 parent 294a279 commit a70b446

22 files changed

+1971
-63
lines changed

special-pages/pages/new-tab/app/components/App.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function App() {
4545

4646
return (
4747
<Fragment>
48-
<BackgroundConsumer browser={browser} />
48+
<BackgroundConsumer browser={browser} bg={main} />
4949
<div class={styles.layout} ref={wrapperRef} data-animating={animating} data-drawer-visibility={visibility}>
5050
<main class={cn(styles.main, styles.mainScroller)} data-main-scroller data-theme={main}>
5151
<div class={styles.content}>

special-pages/pages/new-tab/app/components/BackgroundProvider.js

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { h } from 'preact';
1+
import { Fragment, h } from 'preact';
22
import styles from './BackgroundReceiver.module.css';
3+
import { values } from '../customizer/values.js';
34
import { useContext } from 'preact/hooks';
45
import { CustomizerContext } from '../customizer/CustomizerProvider.js';
6+
import { detectThemeFromHex } from '../customizer/utils.js';
57

68
/**
79
* @import { BackgroundVariant, BrowserTheme } from "../../types/new-tab"
@@ -18,12 +20,22 @@ export function inferSchemeFrom(background, browserTheme, system) {
1820
switch (background.kind) {
1921
case 'default':
2022
return { bg: browser, browser };
21-
case 'gradient':
23+
case 'color': {
24+
const color = values.colors[background.value];
25+
return { bg: color.colorScheme, browser };
26+
}
27+
28+
case 'gradient': {
29+
const gradient = values.gradients[background.value];
30+
return { bg: gradient.colorScheme, browser };
31+
}
32+
2233
case 'userImage':
34+
return { bg: background.value.colorScheme, browser };
35+
2336
case 'hex':
24-
console.log('not supported yet!');
37+
return { bg: detectThemeFromHex(background.value), browser };
2538
}
26-
return { bg: browser, browser };
2739
}
2840

2941
/**
@@ -40,23 +52,90 @@ export function themeFromBrowser(browserTheme, system) {
4052

4153
/**
4254
* @param {object} props
55+
* @param {import("@preact/signals").Signal<'light' | 'dark'>} props.bg
4356
* @param {import("@preact/signals").Signal<'light' | 'dark'>} props.browser
4457
*/
45-
export function BackgroundConsumer({ browser }) {
58+
export function BackgroundConsumer({ bg, browser }) {
4659
const { data } = useContext(CustomizerContext);
4760
const background = data.value.background;
4861

4962
switch (background.kind) {
5063
case 'default': {
5164
return <div className={styles.root} data-testid="BackgroundConsumer" data-background-kind="default" data-theme={browser} />;
5265
}
53-
case 'hex':
54-
case 'color':
55-
case 'gradient':
56-
case 'userImage':
66+
case 'hex': {
67+
return (
68+
<div
69+
class={styles.root}
70+
data-animate="true"
71+
data-testid="BackgroundConsumer"
72+
style={{
73+
backgroundColor: background.value,
74+
}}
75+
></div>
76+
);
77+
}
78+
case 'color': {
79+
const color = values.colors[background.value];
80+
return (
81+
<div
82+
class={styles.root}
83+
data-animate="true"
84+
data-background-color={color.hex}
85+
data-testid="BackgroundConsumer"
86+
style={{
87+
backgroundColor: color.hex,
88+
}}
89+
></div>
90+
);
91+
}
92+
case 'gradient': {
93+
const gradient = values.gradients[background.value];
94+
return (
95+
<Fragment key="gradient">
96+
<div
97+
class={styles.root}
98+
data-animate="false"
99+
data-testid="BackgroundConsumer"
100+
style={{
101+
backgroundColor: gradient.fallback,
102+
backgroundImage: `url(${gradient.path})`,
103+
backgroundSize: 'cover',
104+
backgroundRepeat: 'no-repeat',
105+
}}
106+
/>
107+
<div
108+
class={styles.root}
109+
data-animate="false"
110+
style={{
111+
backgroundImage: `url(gradients/grain.png)`,
112+
backgroundRepeat: 'repeat',
113+
opacity: 0.5,
114+
mixBlendMode: 'soft-light',
115+
}}
116+
></div>
117+
</Fragment>
118+
);
119+
}
120+
case 'userImage': {
121+
const img = background.value;
122+
return (
123+
<div
124+
class={styles.root}
125+
data-animate="true"
126+
data-testid="BackgroundConsumer"
127+
style={{
128+
backgroundImage: `url(${img.src})`,
129+
backgroundSize: 'cover',
130+
backgroundRepeat: 'no-repeat',
131+
backgroundPosition: 'center center',
132+
}}
133+
></div>
134+
);
135+
}
57136
default: {
58-
console.warn('not supported yet!');
59-
return <div className={styles.root} data-testid="BackgroundConsumer" data-background-kind="default" data-theme={browser} />;
137+
console.warn('Unreachable!');
138+
return <div className={styles.root}></div>;
60139
}
61140
}
62141
}

special-pages/pages/new-tab/app/customizer/CustomizerProvider.js

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { createContext, h } from 'preact';
2-
import { signal, useSignal } from '@preact/signals';
2+
import { useCallback } from 'preact/hooks';
3+
import { effect, signal, useSignal } from '@preact/signals';
34
import { useThemes } from './themes.js';
45

56
/**
67
* @typedef {import('../../types/new-tab.js').CustomizerData} CustomizerData
8+
* @typedef {import('../../types/new-tab.js').BackgroundData} BackgroundData
9+
* @typedef {import('../../types/new-tab.js').ThemeData} ThemeData
10+
* @typedef {import('../../types/new-tab.js').UserImageData} UserImageData
11+
* @typedef {import('../service.hooks.js').State<CustomizerData, undefined>} State
12+
* @typedef {import('../service.hooks.js').Events<CustomizerData, undefined>} Events
713
*/
814

915
/**
@@ -24,6 +30,17 @@ export const CustomizerContext = createContext({
2430
userColor: null,
2531
theme: 'system',
2632
}),
33+
/** @type {(bg: BackgroundData) => void} */
34+
select: (bg) => {},
35+
upload: () => {},
36+
/**
37+
* @type {(theme: ThemeData) => void}
38+
*/
39+
setTheme: (theme) => {},
40+
/**
41+
* @type {(id: string) => void}
42+
*/
43+
deleteImage: (id) => {},
2744
});
2845

2946
/**
@@ -40,10 +57,56 @@ export function CustomizerProvider({ service, initialData, children }) {
4057
const data = useSignal(initialData);
4158
const { main, browser } = useThemes(data);
4259

43-
// todo: add data subscriptions here
60+
effect(() => {
61+
const unsub = service.onBackground((evt) => {
62+
data.value = { ...data.value, background: evt.data.background };
63+
});
64+
const unsub1 = service.onTheme((evt) => {
65+
data.value = { ...data.value, theme: evt.data.theme };
66+
});
67+
const unsub2 = service.onImages((evt) => {
68+
data.value = { ...data.value, userImages: evt.data.userImages };
69+
});
70+
const unsub3 = service.onColor((evt) => {
71+
data.value = { ...data.value, userColor: evt.data.userColor };
72+
});
73+
74+
return () => {
75+
unsub();
76+
unsub1();
77+
unsub2();
78+
unsub3();
79+
};
80+
});
81+
82+
/** @type {(bg: BackgroundData) => void} */
83+
const select = useCallback(
84+
(bg) => {
85+
service.setBackground(bg);
86+
},
87+
[service],
88+
);
89+
90+
const upload = useCallback(() => {
91+
service.upload();
92+
}, [service]);
93+
94+
const setTheme = useCallback(
95+
(theme) => {
96+
service.setTheme(theme);
97+
},
98+
[service],
99+
);
100+
101+
const deleteImage = useCallback(
102+
(id) => {
103+
service.deleteImage(id);
104+
},
105+
[service],
106+
);
44107

45108
return (
46-
<CustomizerContext.Provider value={{ data }}>
109+
<CustomizerContext.Provider value={{ data, select, upload, setTheme, deleteImage }}>
47110
<CustomizerThemesContext.Provider value={{ main, browser }}>{children}</CustomizerThemesContext.Provider>
48111
</CustomizerContext.Provider>
49112
);

0 commit comments

Comments
 (0)