Overview
The Icons component manages the desktop icon grid with full selection support including single-click selection and drag-to-select rubber band functionality.
Component Structure
function Icons ({
icons , // Array of icon data
onMouseDown , // Single icon click handler
onDoubleClick , // Double-click to open
displayFocus , // Whether to show focus state
mouse , // Mouse position from useMouse hook
selecting , // Rubber band selection state
setSelectedIcons , // Callback to update selection
}) {
return (
< IconsContainer >
{ icons . map ( iconData => (
< StyledIcon
key = { iconData . id }
{ ... iconData }
displayFocus = { displayFocus }
onMouseDown = { onMouseDown }
onDoubleClick = { onDoubleClick }
/>
)) }
</ IconsContainer >
);
}
Source: src/WinXP/Icons/index.jsx:4-46
Icon Layout
Icons are arranged in a vertical column layout using flexbox:
const IconsContainer = styled . div `
position: absolute;
top: 40px;
left: 40px;
right: 20px;
bottom: 30px;
display: flex;
flex-direction: column; // Vertical layout
flex-wrap: wrap; // Wrap to next column
align-content: flex-start;
overflow-y: auto;
overflow-x: hidden;
gap: 15px;
pointer-events: none; // Let clicks pass through container
` ;
Source: src/WinXP/Icons/index.jsx:96-112
The container has pointer-events: none to allow desktop clicks, while individual icons have pointer-events: auto.
Icon State
Each icon has the following structure:
{
id : 0 , // Unique identifier
icon : ie , // Icon image source
title : 'Internet Explorer' , // Display name
component : InternetExplorer , // Component to launch
isFocus : false , // Selection state
appName : 'Internet Explorer' , // App identifier
}
Source: src/WinXP/apps/index.jsx:136-143
Default Icons
export const defaultIconState = [
{
id: genId (),
icon: ie ,
title: 'Internet Explorer' ,
component: InternetExplorer ,
isFocus: false ,
appName: 'Internet Explorer' ,
},
{
id: genId (),
icon: mine ,
title: 'Minesweeper' ,
component: WrappedMinesweeper ,
isFocus: false ,
appName: 'Minesweeper' ,
},
{
id: genId (),
icon: computerLarge ,
title: 'My Computer' ,
component: MyComputer ,
isFocus: false ,
appName: 'My Computer' ,
},
// ... more icons
];
Source: src/WinXP/apps/index.jsx:135-232
Single Icon Selection
Clicking an icon focuses it and blurs others:
function onMouseDownIcon ( id ) {
dispatch ({ type: FOCUS_ICON , payload: id });
}
Source: src/WinXP/index.jsx:123-125
Reducer Logic
case FOCUS_ICON : {
const icons = state . icons . map ( icon => ({
... icon ,
isFocus: icon . id === action . payload , // Only focused icon is true
}));
return {
... state ,
focusing: FOCUSING . ICON ,
icons ,
};
}
Source: src/WinXP/reducer.js:118-128
Double-Click to Open
Double-clicking an icon launches the application:
function onDoubleClickIcon ( component ) {
const appSetting = Object . values ( appSettings ). find (
setting => setting . component === component ,
);
if ( appSetting ) {
dispatch ({ type: ADD_APP , payload: appSetting });
}
}
Source: src/WinXP/index.jsx:126-133
Double-clicking the icon graphic or text will both launch the application.
Rubber Band Selection
Drag-to-select functionality with a visual selection box.
Initiating Selection
Clicking on the desktop background starts selection:
function onMouseDownDesktop ( e ) {
if ( e . target === e . currentTarget ) {
dispatch ({ type: FOCUS_DESKTOP });
dispatch ({
type: START_SELECT ,
payload: { x: mouse . docX , y: mouse . docY },
});
}
}
Source: src/WinXP/index.jsx:192-200
Selection State
case START_SELECT :
return {
... state ,
focusing: FOCUSING . DESKTOP ,
icons: state . icons . map ( icon => ({
... icon ,
isFocus: false , // Clear all selections
})),
selecting: action . payload , // Store start position
};
Source: src/WinXP/reducer.js:149-158
Visual Selection Box
The DashedBox component renders the selection rectangle:
function DashedBox ({ mouse , startPos }) {
function getRect () {
return {
x: Math . min ( startPos . x , mouse . docX ),
y: Math . min ( startPos . y , mouse . docY ),
w: Math . abs ( startPos . x - mouse . docX ),
h: Math . abs ( startPos . y - mouse . docY ),
};
}
if ( startPos ) {
const { x , y , w , h } = getRect ();
return (
< div
style = { {
transform: `translate( ${ x } px, ${ y } px)` ,
width: w ,
height: h ,
position: 'absolute' ,
border: `1px dotted gray` ,
} }
/>
);
}
return null ;
}
Source: src/components/DashedBox/index.jsx
Collision Detection
Icons measure their positions and update selection based on overlap:
const [ iconsRect , setIconsRect ] = useState ([]);
// Each icon measures itself on mount
function measure ( rect ) {
if ( iconsRect . find ( r => r . id === rect . id )) return ;
setIconsRect ( prevIconsRect => [ ... prevIconsRect , rect ]);
}
useEffect (() => {
if ( ! selecting ) return ;
const sx = Math . min ( selecting . x , mouse . docX );
const sy = Math . min ( selecting . y , mouse . docY );
const sw = Math . abs ( selecting . x - mouse . docX );
const sh = Math . abs ( selecting . y - mouse . docY );
// Find icons intersecting selection box
const selectedIds = iconsRect
. filter ( rect => {
const { x , y , w , h } = rect ;
return x - sx < sw && sx - x < w && y - sy < sh && sy - y < h ;
})
. map ( icon => icon . id );
setSelectedIcons ( selectedIds );
}, [ iconsRect , setSelectedIcons , selecting , mouse . docX , mouse . docY ]);
Source: src/WinXP/Icons/index.jsx:13-31
Ending Selection
function onMouseUpDesktop () {
if ( state . selecting ) {
dispatch ({ type: END_SELECT });
}
}
case END_SELECT :
return {
... state ,
selecting: null , // Clear selection box
};
Source: src/WinXP/index.jsx:201-205, src/WinXP/reducer.js:159-163
Multi-Selection
Multiple icons can be selected via rubber band:
case SELECT_ICONS : {
const icons = state . icons . map ( icon => ({
... icon ,
isFocus: action . payload . includes ( icon . id ), // Check if in array
}));
return {
... state ,
icons ,
focusing: FOCUSING . ICON ,
};
}
Source: src/WinXP/reducer.js:129-139
Icon Styling
Basic Structure
function Icon ({ title , icon , className , onMouseDown , onDoubleClick }) {
return (
< div
className = { className }
onMouseDown = { onMouseDown }
onDoubleClick = { onDoubleClick }
>
< div className = { ` ${ className } __img__container` } >
< img src = { icon } alt = { title } className = { ` ${ className } __img` } />
</ div >
< div className = { ` ${ className } __text__container` } >
< div className = { ` ${ className } __text` } > { title } </ div >
</ div >
</ div >
);
}
Source: src/WinXP/Icons/index.jsx:79-93
Focus State
const StyledIcon = styled ( Icon ) `
width: 70px;
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
pointer-events: auto;
&__text {
padding: 0 3px 2px;
background-color: ${ ({ isFocus , displayFocus }) =>
isFocus && displayFocus ? '#0b61ff' : 'transparent' } ;
text-align: center;
flex-shrink: 1;
}
&__img__container {
width: 30px;
height: 30px;
filter: ${ ({ isFocus , displayFocus }) =>
isFocus && displayFocus ? 'drop-shadow(0 0 blue)' : '' } ;
}
&__img {
width: 30px;
height: 30px;
opacity: ${ ({ isFocus , displayFocus }) =>
isFocus && displayFocus ? 0.5 : 1 } ;
}
` ;
Source: src/WinXP/Icons/index.jsx:114-162
The displayFocus prop controls whether focus styling is shown. It’s true only when focusing === FOCUSING.ICON.
Clearing Icon Selection
Clicking the desktop, windows, or taskbar clears icon selection:
case FOCUS_DESKTOP :
return {
... state ,
focusing: FOCUSING . DESKTOP ,
icons: state . icons . map ( icon => ({
... icon ,
isFocus: false , // Clear all selections
})),
};
Source: src/WinXP/reducer.js:140-148
Icon Position Measurement
Icons measure their DOM position for selection detection:
const ref = useRef ( null );
useEffect (() => {
const target = ref . current ;
if ( ! target ) return ;
const { left , top , width , height } = ref . current . getBoundingClientRect ();
const posX = left + window . scrollX ;
const posY = top + window . scrollY ;
if ( typeof measure === 'function' ) {
measure ({ id , x: posX , y: posY , w: width , h: height });
}
}, [ id , measure ]);
Source: src/WinXP/Icons/index.jsx:69-78
Usage Example
import { Icons } from './WinXP/Icons' ;
import { DashedBox } from './components' ;
import useMouse from 'react-use/lib/useMouse' ;
function Desktop () {
const ref = useRef ( null );
const mouse = useMouse ( ref );
const [ state , dispatch ] = useReducer ( reducer , initState );
const onIconsSelected = useCallback (
iconIds => dispatch ({ type: SELECT_ICONS , payload: iconIds }),
[],
);
return (
< Container ref = { ref } >
< Icons
icons = { state . icons }
onMouseDown = { id => dispatch ({ type: FOCUS_ICON , payload: id }) }
onDoubleClick = { component => {
const app = appSettings [ component ];
dispatch ({ type: ADD_APP , payload: app });
} }
displayFocus = { state . focusing === FOCUSING . ICON }
mouse = { mouse }
selecting = { state . selecting }
setSelectedIcons = { onIconsSelected }
/>
< DashedBox startPos = { state . selecting } mouse = { mouse } />
</ Container >
);
}
Next Steps
Desktop Overview Return to desktop architecture
Windows Learn about window management
Taskbar Explore the taskbar and start menu