Skip to content

Commit bbdf594

Browse files
authored
ntp: shipreview changes to favorites (#1405)
* ntp: shipreview changes to favorites * compare id
1 parent 562ebfa commit bbdf594

File tree

16 files changed

+538
-222
lines changed

16 files changed

+538
-222
lines changed

special-pages/pages/new-tab/app/favorites/color.js

Lines changed: 0 additions & 55 deletions
This file was deleted.

special-pages/pages/new-tab/app/favorites/components/Favorites.js

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Fragment, h } from 'preact';
1+
import { createContext, Fragment, h } from 'preact';
22
import { useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'preact/hooks';
33
import { memo } from 'preact/compat';
44
import cn from 'classnames';
@@ -10,7 +10,7 @@ import { usePlatformName } from '../../settings.provider.js';
1010
import { useDropzoneSafeArea } from '../../dropzone.js';
1111
import { TileRow } from './TileRow.js';
1212
import { FavoritesContext } from './FavoritesProvider.js';
13-
import { CustomizerContext } from '../../customizer/CustomizerProvider.js';
13+
import { CustomizerContext, CustomizerThemesContext } from '../../customizer/CustomizerProvider.js';
1414
import { useComputed } from '@preact/signals';
1515

1616
/**
@@ -25,6 +25,7 @@ export const ROW_CAPACITY = 6;
2525
*/
2626
const ITEM_HEIGHT = 96;
2727
const ROW_GAP = 8;
28+
export const FavoritesThemeContext = createContext(/** @type {"light"|"dark"} */ ('light'));
2829

2930
/**
3031
* Favorites Grid.
@@ -49,45 +50,50 @@ export function Favorites({ gridRef, favorites, expansion, toggle, openContextMe
4950
const rowHeight = ITEM_HEIGHT + ROW_GAP;
5051
const canToggleExpansion = favorites.length >= ROW_CAPACITY;
5152
const { data } = useContext(CustomizerContext);
53+
const { main } = useContext(CustomizerThemesContext);
5254
const kind = useComputed(() => data.value.background.kind);
5355

5456
return (
55-
<div
56-
class={cn(styles.root, !canToggleExpansion && styles.noExpansionBtn)}
57-
data-testid="FavoritesConfigured"
58-
data-background-kind={kind}
59-
>
60-
<VirtualizedGridRows
61-
WIDGET_ID={WIDGET_ID}
62-
favorites={favorites}
63-
rowHeight={rowHeight}
64-
add={add}
65-
expansion={expansion}
66-
openFavorite={openFavorite}
67-
openContextMenu={openContextMenu}
68-
/>
69-
{canToggleExpansion && (
70-
<div
71-
className={cn({
72-
[styles.showhide]: true,
73-
[styles.showhideVisible]: canToggleExpansion,
74-
})}
75-
>
76-
<ShowHideButton
77-
buttonAttrs={{
78-
'aria-expanded': expansion === 'expanded',
79-
'aria-pressed': expansion === 'expanded',
80-
'aria-controls': WIDGET_ID,
81-
id: TOGGLE_ID,
82-
}}
83-
text={
84-
expansion === 'expanded' ? t('favorites_show_less') : t('favorites_show_more', { count: String(hiddenCount) })
85-
}
86-
onClick={toggle}
87-
/>
88-
</div>
89-
)}
90-
</div>
57+
<FavoritesThemeContext.Provider value={main.value}>
58+
<div
59+
class={cn(styles.root, !canToggleExpansion && styles.noExpansionBtn)}
60+
data-testid="FavoritesConfigured"
61+
data-background-kind={kind}
62+
>
63+
<VirtualizedGridRows
64+
WIDGET_ID={WIDGET_ID}
65+
favorites={favorites}
66+
rowHeight={rowHeight}
67+
add={add}
68+
expansion={expansion}
69+
openFavorite={openFavorite}
70+
openContextMenu={openContextMenu}
71+
/>
72+
{canToggleExpansion && (
73+
<div
74+
className={cn({
75+
[styles.showhide]: true,
76+
[styles.showhideVisible]: canToggleExpansion,
77+
})}
78+
>
79+
<ShowHideButton
80+
buttonAttrs={{
81+
'aria-expanded': expansion === 'expanded',
82+
'aria-pressed': expansion === 'expanded',
83+
'aria-controls': WIDGET_ID,
84+
id: TOGGLE_ID,
85+
}}
86+
text={
87+
expansion === 'expanded'
88+
? t('favorites_show_less')
89+
: t('favorites_show_more', { count: String(hiddenCount) })
90+
}
91+
onClick={toggle}
92+
/>
93+
</div>
94+
)}
95+
</div>
96+
</FavoritesThemeContext.Provider>
9197
);
9298
}
9399

@@ -145,7 +151,7 @@ function VirtualizedGridRows({ WIDGET_ID, rowHeight, favorites, expansion, openF
145151
onContextMenu={getContextMenuHandler(openContextMenu)}
146152
onClick={getOnClickHandler(openFavorite, platformName)}
147153
>
148-
{rows.length === 0 && <TileRow key={'empty-rows'} items={[]} topOffset={0} add={add} />}
154+
{rows.length === 0 && <TileRow key={'empty-rows'} items={[]} topOffset={0} add={add} visibility={'visible'} />}
149155
{rows.length > 0 && <Inner rows={rows} safeAreaRef={safeAreaRef} rowHeight={rowHeight} add={add} />}
150156
</div>
151157
);
@@ -166,6 +172,7 @@ function VirtualizedGridRows({ WIDGET_ID, rowHeight, favorites, expansion, openF
166172
function Inner({ rows, safeAreaRef, rowHeight, add }) {
167173
const { onConfigChanged, state } = useContext(FavoritesContext);
168174
const [expansion, setExpansion] = useState(state.config?.expansion || 'collapsed');
175+
const documentVisibility = useDocumentVisibility();
169176

170177
// force the children to be rendered after the main thread is cleared
171178
useEffect(() => {
@@ -242,12 +249,17 @@ function Inner({ rows, safeAreaRef, rowHeight, add }) {
242249
{ signal: controller.signal },
243250
);
244251

245-
// when the content-tube grows, re-calc the layout
246-
const resizer = new ResizeObserver(() => {
247-
requestAnimationFrame(() => {
248-
updateGlobals(mainScroller.scrollTop);
249-
setVisibleRowsForOffset(mainScroller.scrollTop);
250-
});
252+
let lastHeight;
253+
const resizer = new ResizeObserver((entries) => {
254+
const first = entries[0];
255+
if (!first) return;
256+
if (first.contentRect.height !== lastHeight) {
257+
lastHeight = first.contentRect.height;
258+
requestAnimationFrame(() => {
259+
updateGlobals(mainScroller.scrollTop);
260+
setVisibleRowsForOffset(mainScroller.scrollTop);
261+
});
262+
}
251263
});
252264
resizer.observe(contentTube);
253265

@@ -285,12 +297,34 @@ function Inner({ rows, safeAreaRef, rowHeight, add }) {
285297
{subsetOfRowsToRender.map((items, rowIndex) => {
286298
const topOffset = expansion === 'expanded' ? (start + rowIndex) * rowHeight : 0;
287299
const keyed = `-${start + rowIndex}-`;
288-
return <TileRow key={keyed} dropped={dropped} items={items} topOffset={topOffset} add={add} />;
300+
return (
301+
<TileRow key={keyed} dropped={dropped} items={items} topOffset={topOffset} add={add} visibility={documentVisibility} />
302+
);
289303
})}
290304
</Fragment>
291305
);
292306
}
293307

308+
function useDocumentVisibility() {
309+
/** @type {Document['visibilityState']} */
310+
const initial = document.visibilityState;
311+
const [documentVisibility, setDocumentVisibility] = useState(/** @type {Document['visibilityState']} */ (initial));
312+
313+
useEffect(() => {
314+
const handleVisibilityChange = () => {
315+
setDocumentVisibility(document.visibilityState);
316+
};
317+
318+
document.addEventListener('visibilitychange', handleVisibilityChange);
319+
320+
return () => {
321+
document.removeEventListener('visibilitychange', handleVisibilityChange);
322+
};
323+
}, []);
324+
325+
return documentVisibility;
326+
}
327+
294328
/**
295329
* Handle right-clicks
296330
*

special-pages/pages/new-tab/app/favorites/components/PragmaticDND.js

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { monitorForExternal, dropTargetForExternal } from '@atlaskit/pragmatic-d
99
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
1010
import { getHTML } from '@atlaskit/pragmatic-drag-and-drop/external/html';
1111
import { DDG_MIME_TYPE } from '../constants.js';
12+
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
13+
import { centerUnderPointer } from '@atlaskit/pragmatic-drag-and-drop/element/center-under-pointer';
1214

1315
/** @type {import("preact").Context<symbol>} */
1416
const InstanceIdContext = createContext(getInstanceId());
@@ -93,23 +95,19 @@ function useGridState(favorites, itemsDidReOrder, instanceId) {
9395
}
9496

9597
const destinationSrc = target.data.url;
96-
const startSrc = source.data.url;
98+
const destinationId = target.data.id;
9799
const startId = source.data.id;
98100

99101
if (typeof startId !== 'string') {
100-
return console.warn('could not access the id');
102+
return console.warn('could not access startId');
101103
}
102104

103105
if (typeof destinationSrc !== 'string') {
104106
return console.warn('could not access the destinationSrc');
105107
}
106108

107-
if (typeof startSrc !== 'string') {
108-
return console.warn('could not access the startSrc');
109-
}
110-
111-
const startIndex = favorites.findIndex((item) => item.url === startSrc);
112-
let indexOfTarget = favorites.findIndex((item) => item.url === destinationSrc);
109+
const startIndex = favorites.findIndex((item) => item.id === startId);
110+
let indexOfTarget = favorites.findIndex((item) => item.id === destinationId);
113111

114112
if (indexOfTarget === -1 && destinationSrc.includes('PLACEHOLDER-URL')) {
115113
indexOfTarget = favorites.length;
@@ -164,9 +162,10 @@ function useGridState(favorites, itemsDidReOrder, instanceId) {
164162
/**
165163
* @param {string} url
166164
* @param {string} id
165+
* @param {{kind: "draggable"; onPreview: (div: HTMLDivElement) => void} | {kind: "target"}} opts
167166
* @return {{ ref: import("preact").RefObject<any>; state: DNDState }}
168167
*/
169-
export function useItemState(url, id) {
168+
export function useItemState(url, id, opts) {
170169
const instanceId = useContext(InstanceIdContext);
171170
/** @type {import("preact").Ref<HTMLAnchorElement>} */
172171
const ref = useRef(null);
@@ -175,9 +174,10 @@ export function useItemState(url, id) {
175174
useEffect(() => {
176175
const el = ref.current;
177176
if (!el) throw new Error('unreachable');
177+
let draggableCleanup = () => {};
178178

179-
return combine(
180-
draggable({
179+
if (opts.kind === 'draggable') {
180+
draggableCleanup = draggable({
181181
element: el,
182182
getInitialData: () => ({ type: 'grid-item', url, id, instanceId }),
183183
getInitialDataForExternal: () => ({
@@ -186,7 +186,27 @@ export function useItemState(url, id) {
186186
}),
187187
onDragStart: () => setState({ type: 'dragging' }),
188188
onDrop: () => setState({ type: 'idle' }),
189-
}),
189+
onGenerateDragPreview: ({ nativeSetDragImage, source }) => {
190+
setCustomNativeDragPreview({
191+
getOffset: ({ container }) => centerUnderPointer({ container }),
192+
render: ({ container }) => {
193+
const clone = /** @type {HTMLElement} */ (source.element.cloneNode(true));
194+
const outer = document.createElement('div');
195+
opts.onPreview(outer);
196+
outer.appendChild(clone);
197+
container.appendChild(outer);
198+
return () => {
199+
container.removeChild(outer);
200+
};
201+
},
202+
nativeSetDragImage,
203+
});
204+
},
205+
});
206+
}
207+
208+
return combine(
209+
draggableCleanup,
190210
dropTargetForExternal({
191211
element: el,
192212
canDrop: ({ source }) => {

0 commit comments

Comments
 (0)