Skip to content

Commit

Permalink
ntp: customizer messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Shane Osbourne committed Dec 4, 2024
1 parent 0548bdf commit fe3a5fd
Show file tree
Hide file tree
Showing 59 changed files with 2,360 additions and 82 deletions.
62 changes: 35 additions & 27 deletions special-pages/pages/new-tab/app/components/App.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { h } from 'preact';
import { Fragment, h } from 'preact';
import cn from 'classnames';
import styles from './App.module.css';
import { useCustomizerDrawerSettings, usePlatformName } from '../settings.provider.js';
Expand All @@ -7,6 +7,7 @@ import { useGlobalDropzone } from '../dropzone.js';
import { Customizer, CustomizerButton, CustomizerMenuPositionedFixed, useContextMenu } from '../customizer/components/Customizer.js';
import { useDrawer, useDrawerControls } from './Drawer.js';
import { CustomizerDrawer } from '../customizer/components/CustomizerDrawer.js';
import { BackgroundConsumer, BackgroundProvider } from './BackgroundProvider.js';

/**
* Renders the App component.
Expand All @@ -24,35 +25,42 @@ export function App({ children }) {
useContextMenu();

const { buttonRef, wrapperRef, visibility, displayChildren, hidden, buttonId, drawerId } = useDrawer();
const { toggle, close } = useDrawerControls();
const { toggle } = useDrawerControls();

return (
<div class={cn(styles.layout)} ref={wrapperRef} data-drawer-visibility={visibility}>
<main class={cn(styles.main)} data-customizer-kind={customizerKind}>
<div class={styles.tube} data-platform={platformName}>
<WidgetList />
<CustomizerMenuPositionedFixed>
{customizerKind === 'menu' && <Customizer />}
{customizerKind === 'drawer' && (
<CustomizerButton
buttonId={buttonId}
menuId={drawerId}
toggleMenu={toggle}
buttonRef={buttonRef}
isOpen={false}
/>
)}
</CustomizerMenuPositionedFixed>
{children}
</div>
</main>
<Fragment>
{customizerKind === 'drawer' && (
<aside id={drawerId} class={styles.aside} aria-hidden={hidden}>
<div class={styles.asideContent}>
<CustomizerDrawer onClose={close} wrapperRef={wrapperRef} displayChildren={displayChildren} />
</div>
</aside>
<BackgroundProvider>
<BackgroundConsumer />
</BackgroundProvider>
)}
</div>
<div class={cn(styles.layout)} ref={wrapperRef} data-drawer-visibility={visibility}>
<main class={cn(styles.main)} data-customizer-kind={customizerKind}>
<div class={styles.tube} data-platform={platformName}>
<WidgetList />
<CustomizerMenuPositionedFixed>
{customizerKind === 'menu' && <Customizer />}
{customizerKind === 'drawer' && (
<CustomizerButton
buttonId={buttonId}
menuId={drawerId}
toggleMenu={toggle}
buttonRef={buttonRef}
isOpen={false}
/>
)}
</CustomizerMenuPositionedFixed>
{children}
</div>
</main>
{customizerKind === 'drawer' && (
<aside id={drawerId} class={styles.aside} aria-hidden={hidden}>
<div class={styles.asideContent}>
<CustomizerDrawer displayChildren={displayChildren} />
</div>
</aside>
)}
</div>
</Fragment>
);
}
2 changes: 1 addition & 1 deletion special-pages/pages/new-tab/app/components/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ body:has([data-reset-layout="true"]) .tube {
}
}


.active {}

.aside {
Expand All @@ -69,5 +70,4 @@ body:has([data-reset-layout="true"]) .tube {
box-sizing: border-box;
height: 100vh;
width: var(--ntp-drawer-width);
padding: var(--sp-2);
}
100 changes: 100 additions & 0 deletions special-pages/pages/new-tab/app/components/BackgroundProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { createContext, Fragment, h } from 'preact';
import styles from './BackgroundReceiver.module.css';
import { values } from '../customizer/values.js';
import { computed, signal } from '@preact/signals';
import { useContext } from 'preact/hooks';
import { CustomizerContext } from '../customizer/CustomizerProvider.js';

/**
* @import { BackgroundVariant } from "../../types/new-tab"
*/

const BackgroundContext = createContext({
/** @type {import("@preact/signals").Signal<BackgroundVariant>} */
current: signal({ kind: 'default' }),
});

/**
* @param {object} props
* @param {import("preact").ComponentChild} props.children
*/
export function BackgroundProvider({ children }) {
const { data } = useContext(CustomizerContext);
const bg = computed(() => data.value.background);
return <BackgroundContext.Provider value={{ current: bg }}>{children}</BackgroundContext.Provider>;
}

/**
*
*/
export function BackgroundConsumer() {
const { current } = useContext(BackgroundContext);
const background = current.value;
if (background === null) {
return <div class={styles.root}></div>;
}
switch (background.kind) {
case 'hex': {
return (
<div
class={styles.root}
style={{
backgroundColor: background.value,
}}
></div>
);
}
case 'color': {
const color = values.colors[background.value];
return (
<div
class={styles.root}
style={{
backgroundColor: color.hex,
}}
></div>
);
}
case 'gradient': {
const gradient = values.gradients[background.value];
return (
<Fragment key="gradient">
<div
class={styles.root}
style={{
backgroundImage: `url(${gradient.path})`,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
}}
/>
<div
class={styles.root}
style={{
backgroundImage: `url(gradients/grain.png)`,
backgroundRepeat: 'repeat',
opacity: 0.5,
mixBlendMode: 'soft-light',
}}
></div>
</Fragment>
);
}
case 'userImage': {
const img = background.value;
return (
<div
class={styles.root}
style={{
backgroundImage: `url(${img.src})`,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
}}
></div>
);
}
default: {
throw new Error('Unreachable!');
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.root {
position: fixed;
z-index: 0;
inset: 0;
width: 100vw;
height: 100vh;
transition: all .3s;
}
4 changes: 2 additions & 2 deletions special-pages/pages/new-tab/app/components/DismissButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { Cross } from './Icons';
import { useTypedTranslation } from '../types';
import styles from './DismissButton.module.css';

/*
/**
* @param {object} props
* @param {string} [props.className]
* @param {() => void} props.onClick
* @param {() => void} [props.onClick]
*/
export function DismissButton({ className, onClick }) {
const { t } = useTypedTranslation();
Expand Down
35 changes: 35 additions & 0 deletions special-pages/pages/new-tab/app/components/Icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,38 @@ export function Cross() {
</svg>
);
}

export function CircleCheck() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1635_18497)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12ZM17.5737 9.25013C17.9189 8.86427 17.886 8.27159 17.5001 7.92635C17.1143 7.5811 16.5216 7.61403 16.1764 7.99989L10.5319 14.3084L7.85061 11.0313C7.52274 10.6306 6.9321 10.5716 6.53137 10.8994C6.13064 11.2273 6.07157 11.8179 6.39944 12.2187L9.77444 16.3437C9.94792 16.5557 10.2054 16.6812 10.4793 16.6873C10.7532 16.6933 11.016 16.5793 11.1987 16.3751L17.5737 9.25013Z"
fill="black"
fill-opacity="0.6"
/>
</g>
<defs>
<clipPath id="clip0_1635_18497">
<rect width="24" height="24" fill="white" />
</clipPath>
</defs>
</svg>
);
}

export function Picker() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M20.5588 3.44118C19.3873 2.26961 17.4878 2.26961 16.3162 3.44118L16.1527 3.60466C16.1473 3.61004 16.1418 3.61544 16.1364 3.62087L12.5858 7.17141L11.7071 6.29268C11.3166 5.90216 10.6834 5.90216 10.2929 6.29268C9.90239 6.68321 9.90239 7.31637 10.2929 7.7069L11.1717 8.58568L3.44124 16.3161C2.26967 17.4877 2.26967 19.3872 3.44124 20.5588C4.61281 21.7304 6.51231 21.7304 7.68388 20.5588L15.4143 12.8283L16.2929 13.7069C16.6834 14.0974 17.3166 14.0974 17.7071 13.7069C18.0977 13.3164 18.0977 12.6832 17.7071 12.2927L16.8286 11.4141L20.5588 7.68382C21.7304 6.51225 21.7304 4.61275 20.5588 3.44118ZM12.5859 9.9999L4.85545 17.7304C4.46493 18.1209 4.46493 18.754 4.85545 19.1446C5.24598 19.5351 5.87914 19.5351 6.26967 19.1446L14.0001 11.4141L12.5859 9.9999Z"
fill="currentColor"
fill-opacity="0.84"
/>
</svg>
);
}
92 changes: 92 additions & 0 deletions special-pages/pages/new-tab/app/customizer/CustomizerProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { createContext, h } from 'preact';
import { useCallback } from 'preact/hooks';
import { effect, signal, useSignal } from '@preact/signals';

/**
* @typedef {import('../../types/new-tab.js').CustomizerData} CustomizerData
* @typedef {import('../service.hooks.js').State<CustomizerData, undefined>} State
* @typedef {import('../service.hooks.js').Events<CustomizerData, undefined>} Events
*/

/**
* These are the values exposed to consumers.
*/
export const CustomizerContext = createContext({
/** @type {import("@preact/signals").Signal<CustomizerData>} */
data: signal({
background: { kind: 'default' },
userImages: [],
theme: 'system',
}),
/** @type {(bg: CustomizerData['background']) => void} */
select: (bg) => {},
upload: () => {},
/**
* @type {(theme: import('../../types/new-tab').CustomizerData['theme']) => void}
*/
setTheme: (theme) => {},
/**
* @type {(id: string) => void}
*/
deleteImage: (id) => {},
});

/**
* A data provider that will use `RMFService` to fetch data, subscribe
* to updates and modify state.
*
* @param {Object} props
* @param {import("./customizer.service.js").CustomizerService} props.service
* @param {CustomizerData} props.initialData
* @param {import("preact").ComponentChild} props.children
*/
export function CustomizerProvider({ service, initialData, children }) {
// const [state, dispatch] = useReducer(withLog('RMFProvider', reducer), initial)
const data = useSignal(initialData);

effect(() => {
const unsub = service.onBackground((evt) => {
data.value = { ...data.value, background: evt.data };
});
const unsub1 = service.onTheme((evt) => {
data.value = { ...data.value, theme: evt.data };
});
const unsub2 = service.onImages((evt) => {
data.value = { ...data.value, userImages: evt.data };
});

return () => {
unsub();
unsub1();
unsub2();
};
});

/** @type {(bg: CustomizerData['background']) => void} */
const select = useCallback(
(bg) => {
service.setBackground(bg);
},
[service],
);

const upload = useCallback(() => {
service.upload();
}, [service]);

const setTheme = useCallback(
(theme) => {
service.setTheme(theme);
},
[service],
);

const deleteImage = useCallback(
(id) => {
service.deleteImage(id);
},
[service],
);

return <CustomizerContext.Provider value={{ data, select, upload, setTheme, deleteImage }}>{children}</CustomizerContext.Provider>;
}
Loading

0 comments on commit fe3a5fd

Please sign in to comment.