Skip to content

ntp: remove legacy menu #1638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 31 additions & 43 deletions special-pages/pages/new-tab/app/components/App.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { Fragment, h } from 'preact';
import cn from 'classnames';
import styles from './App.module.css';
import { useCustomizerDrawerSettings, useCustomizerKind, usePlatformName } from '../settings.provider.js';
import { useCustomizerDrawerSettings, usePlatformName } from '../settings.provider.js';
import { WidgetList } from '../widget-list/WidgetList.js';
import { useGlobalDropzone } from '../dropzone.js';
import {
CustomizerMenu,
CustomizerButton,
CustomizerMenuPositionedFixed,
useContextMenu,
} from '../customizer/components/CustomizerMenu.js';
import { CustomizerButton, CustomizerMenuPositionedFixed, useContextMenu } from '../customizer/components/CustomizerMenu.js';
import { useDrawer, useDrawerControls } from './Drawer.js';
import { CustomizerDrawer } from '../customizer/components/CustomizerDrawer.js';
import { BackgroundConsumer } from './BackgroundProvider.js';
Expand All @@ -28,8 +23,6 @@ export function App() {
const platformName = usePlatformName();
const customizerDrawer = useCustomizerDrawerSettings();

const customizerKind = useCustomizerKind();

useGlobalDropzone();
useContextMenu();

Expand Down Expand Up @@ -63,42 +56,37 @@ export function App() {
</main>
<div class={styles.themeContext} data-theme={main}>
<CustomizerMenuPositionedFixed>
{customizerKind === 'menu' && <CustomizerMenu />}
{customizerKind === 'drawer' && (
<CustomizerButton
buttonId={buttonId}
menuId={drawerId}
toggleMenu={toggle}
buttonRef={buttonRef}
isOpen={isOpen}
kind={'drawer'}
/>
)}
<CustomizerButton
buttonId={buttonId}
menuId={drawerId}
toggleMenu={toggle}
buttonRef={buttonRef}
isOpen={isOpen}
kind={'drawer'}
/>
</CustomizerMenuPositionedFixed>
</div>
{customizerKind === 'drawer' && (
<aside
class={cn(styles.aside, styles.asideLayout, styles.asideScroller)}
tabindex={tabIndex}
aria-hidden={hidden}
data-theme={browser}
data-browser-panel
ref={asideRef}
>
<div class={styles.asideContent}>
<InlineErrorBoundary
context={'Customizer Drawer'}
fallback={(message) => (
<div class={styles.paddedError}>
<p>{message}</p>
</div>
)}
>
<CustomizerDrawer displayChildren={displayChildren} />
</InlineErrorBoundary>
</div>
</aside>
)}
<aside
class={cn(styles.aside, styles.asideLayout, styles.asideScroller)}
tabindex={tabIndex}
aria-hidden={hidden}
data-theme={browser}
data-browser-panel
ref={asideRef}
>
<div class={styles.asideContent}>
<InlineErrorBoundary
context={'Customizer Drawer'}
fallback={(message) => (
<div class={styles.paddedError}>
<p>{message}</p>
</div>
)}
>
<CustomizerDrawer displayChildren={displayChildren} />
</InlineErrorBoundary>
</div>
</aside>
</div>
</Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { h } from 'preact';
import { noop } from '../../utils.js';
import { CustomizerButton } from './CustomizerMenu.js';
import { EmbeddedVisibilityMenu, VisibilityMenu } from './VisibilityMenu.js';
import { EmbeddedVisibilityMenu } from './VisibilityMenu.js';
import { BackgroundSection } from './BackgroundSection.js';
import { ColorSelection } from './ColorSelection.js';
import { GradientSelection } from './GradientSelection.js';
Expand Down Expand Up @@ -68,27 +68,6 @@ export const customizerExamples = {
<MaxContent>
<CustomizerButton isOpen={true} kind="menu" />
<br />
<VisibilityMenu
rows={[
{
id: 'favorites',
title: 'Favorites',
icon: 'star',
toggle: noop('toggle favorites'),
visibility: 'hidden',
index: 0,
},
{
id: 'privacyStats',
title: 'Privacy Stats',
icon: 'shield',
toggle: noop('toggle favorites'),
visibility: 'visible',
index: 1,
},
]}
/>
<br />
<div style="width: 206px; border: 1px dotted black">
<EmbeddedVisibilityMenu
rows={[
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
.root {
position: relative
}

.lowerRightFixed {
position: absolute;
bottom: 1rem;
right: 1rem;
}

.dropdownMenu {
display: none;
position: absolute;
right: 0;
bottom: calc(100% + 10px);
}

.show {
display: block;
}

/** todo: is this a re-usable button, yet? */
.customizeButton {
backdrop-filter: blur(48px);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,67 +1,15 @@
import { h } from 'preact';
import { useEffect, useRef, useState, useCallback, useId } from 'preact/hooks';
import { useEffect } from 'preact/hooks';
import styles from './Customizer.module.css';
import { CustomizeIcon } from '../../components/Icons.js';
import cn from 'classnames';
import { useMessaging, useTypedTranslation } from '../../types.js';
import { VisibilityMenu, VisibilityMenuPopover } from './VisibilityMenu.js';

/**
* @import { Widgets, WidgetConfigItem, WidgetVisibility, VisibilityMenuItem } from '../../../types/new-tab.js'
*/

/**
* Represents the NTP customizer. For now it's just the ability to toggle sections.
*/
export function CustomizerMenu() {
const { setIsOpen, buttonRef, dropdownRef, isOpen } = useDropdown();
const [rowData, setRowData] = useState(/** @type {VisibilityRowData[]} */ ([]));

/**
* Dispatch an event every time the customizer is opened - this
* allows widgets to register themselves and provide titles/icons etc.
*/
const toggleMenu = useCallback(() => {
if (isOpen) return setIsOpen(false);
setRowData(getItems());
setIsOpen(true);
}, [isOpen]);

useEffect(() => {
if (!isOpen) return;
function handler() {
setRowData(getItems());
}
window.addEventListener(CustomizerMenu.UPDATE_EVENT, handler);
return () => {
window.removeEventListener(CustomizerMenu.UPDATE_EVENT, handler);
};
}, [isOpen]);

const MENU_ID = useId();
const BUTTON_ID = useId();

return (
<div class={styles.root} ref={dropdownRef}>
<CustomizerButton
buttonId={BUTTON_ID}
menuId={MENU_ID}
toggleMenu={toggleMenu}
buttonRef={buttonRef}
isOpen={isOpen}
kind={'menu'}
/>
<div id={MENU_ID} class={cn(styles.dropdownMenu, { [styles.show]: isOpen })} aria-labelledby={BUTTON_ID}>
<VisibilityMenuPopover>
<VisibilityMenu rows={rowData} />
</VisibilityMenuPopover>
</div>
</div>
);
}

CustomizerMenu.OPEN_EVENT = 'ntp-customizer-open';
CustomizerMenu.UPDATE_EVENT = 'ntp-customizer-update';
export const OPEN_EVENT = 'ntp-customizer-open';
export const UPDATE_EVENT = 'ntp-customizer-update';

export function getItems() {
/** @type {VisibilityRowData[]} */
Expand All @@ -71,7 +19,7 @@ export function getItems() {
next.push(incoming);
},
};
const event = new CustomEvent(CustomizerMenu.OPEN_EVENT, { detail });
const event = new CustomEvent(OPEN_EVENT, { detail });
window.dispatchEvent(event);
next.sort((a, b) => a.index - b.index);
return next;
Expand Down Expand Up @@ -137,48 +85,6 @@ export function CustomizerMenuPositionedFixed({ children }) {
return <div class={styles.lowerRightFixed}>{children}</div>;
}

function useDropdown() {
/** @type {import("preact").Ref<HTMLDivElement>} */
const dropdownRef = useRef(null);
/** @type {import("preact").Ref<HTMLButtonElement>} */
const buttonRef = useRef(null);

const [isOpen, setIsOpen] = useState(false);

/**
* Event handlers when it's open
*/
useEffect(() => {
if (!isOpen) return;
const handleFocusOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target) && !buttonRef.current?.contains(event.target)) {
setIsOpen(false);
}
};
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains?.(event.target)) {
setIsOpen(false);
}
};
const handleKeyDown = (event) => {
if (event.key === 'Escape') {
setIsOpen(false);
buttonRef.current?.focus?.();
}
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('focusin', handleFocusOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('focusin', handleFocusOutside);
};
}, [isOpen]);

return { dropdownRef, buttonRef, isOpen, setIsOpen };
}

export class VisibilityRowData {
/**
* @param {object} params
Expand Down Expand Up @@ -208,11 +114,11 @@ export function useCustomizer({ title, id, icon, toggle, visibility, index }) {
const handler = (/** @type {CustomEvent<any>} */ e) => {
e.detail.register({ title, id, icon, toggle, visibility, index });
};
window.addEventListener(CustomizerMenu.OPEN_EVENT, handler);
return () => window.removeEventListener(CustomizerMenu.OPEN_EVENT, handler);
window.addEventListener(OPEN_EVENT, handler);
return () => window.removeEventListener(OPEN_EVENT, handler);
}, [title, id, icon, toggle, visibility, index]);

useEffect(() => {
window.dispatchEvent(new Event(CustomizerMenu.UPDATE_EVENT));
window.dispatchEvent(new Event(UPDATE_EVENT));
}, [visibility]);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { h } from 'preact';
import cn from 'classnames';
import { useId, useContext } from 'preact/hooks';
import { useContext } from 'preact/hooks';

import { DuckFoot, Shield } from '../../components/Icons.js';
import styles from './VisibilityMenu.module.css';
Expand All @@ -13,55 +13,6 @@ import { CustomizerThemesContext } from '../CustomizerProvider.js';
* @import { VisibilityRowData } from './CustomizerMenu.js'
*/

/**
* When the button is pressed, we dispatch an event to allow widgets to provide
* meta data like translated titles
*
* @param {object} props
* @param {VisibilityRowData[]} props.rows
*/
export function VisibilityMenu({ rows }) {
const MENU_ID = useId();

return (
<ul className={cn(styles.list)}>
{rows.map((row) => {
return (
<li key={row.id}>
<label className={styles.menuItemLabel} htmlFor={MENU_ID + row.id}>
<input
type="checkbox"
checked={row.visibility === 'visible'}
onChange={() => row.toggle?.(row.id)}
id={MENU_ID + row.id}
class={styles.checkbox}
/>
<span aria-hidden={true} className={styles.checkboxIcon}>
{row.visibility === 'visible' && (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M3.5 9L6 11.5L12.5 5"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
)}
</span>
<span className={styles.svg}>
{row.icon === 'shield' && <DuckFoot />}
{row.icon === 'star' && <Shield />}
</span>
<span>{row.title ?? row.id}</span>
</label>
</li>
);
})}
</ul>
);
}

/**
* @param {object} props
* @param {VisibilityRowData[]} props.rows
Expand Down Expand Up @@ -97,7 +48,3 @@ export function EmbeddedVisibilityMenu({ rows }) {
</ul>
);
}

export function VisibilityMenuPopover({ children }) {
return <div className={styles.dropdownInner}>{children}</div>;
}
Loading
Loading