This commit is contained in:
parent
171fbf1927
commit
8bf3e3b705
1 changed files with 1443 additions and 1497 deletions
162
src/lib/project-state.ts
Normal file
162
src/lib/project-state.ts
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import { nextId, rectangle } from './defaults';
|
||||
import { PROJECT_LIMITS } from './limits';
|
||||
import type {
|
||||
FlooringSettings,
|
||||
PlanFocus,
|
||||
PlanMaterial,
|
||||
PlannerSection,
|
||||
Project,
|
||||
Room,
|
||||
SkirtingSettings,
|
||||
UrlState,
|
||||
} from './types';
|
||||
|
||||
export type ProjectAction =
|
||||
| { type: 'room'; id: string; change: (room: Room) => Room }
|
||||
| { type: 'flooring'; patch: Partial<FlooringSettings> }
|
||||
| { type: 'skirting'; patch: Partial<SkirtingSettings> }
|
||||
| { type: 'tolerance'; value: number }
|
||||
| { type: 'addRoom' }
|
||||
| { type: 'removeRoom'; id: string }
|
||||
| { type: 'visitRoom'; id: string }
|
||||
| { type: 'material'; material: PlanMaterial }
|
||||
| { type: 'select'; material: PlanMaterial; id: string; roomId?: string }
|
||||
| { type: 'closePreview' }
|
||||
| { type: 'search'; text: string; roomId: string; material: PlanMaterial }
|
||||
| { type: 'focus'; focus: PlanFocus; roomId: string }
|
||||
| { type: 'toggleBoard'; key: string }
|
||||
| { type: 'toggleSection'; section: PlannerSection };
|
||||
|
||||
const toggle = <T>(items: T[], value: T) =>
|
||||
items.includes(value) ? items.filter((item) => item !== value) : [...items, value];
|
||||
|
||||
/** Allocation changes invalidate progress and locations together. Renames leave them intact. */
|
||||
function changedPlan(state: UrlState, project: Project): UrlState {
|
||||
return {
|
||||
project: { ...project, completedBoards: [] },
|
||||
view: { ...state.view, selectedPiece: null, search: '', focus: null },
|
||||
};
|
||||
}
|
||||
|
||||
export function reduceProject(state: UrlState, action: ProjectAction): UrlState {
|
||||
const { project, view } = state;
|
||||
switch (action.type) {
|
||||
case 'room': {
|
||||
const room = project.rooms.find((room) => room.id === action.id);
|
||||
if (!room) return state;
|
||||
const next = action.change(room);
|
||||
if (next === room) return state;
|
||||
const updated = {
|
||||
...project,
|
||||
rooms: project.rooms.map((item) => (item === room ? next : item)),
|
||||
};
|
||||
return next.walls === room.walls && next.direction === room.direction
|
||||
? { ...state, project: updated }
|
||||
: changedPlan(state, updated);
|
||||
}
|
||||
case 'flooring':
|
||||
case 'skirting': {
|
||||
if (
|
||||
Object.entries(action.patch).every(
|
||||
([key, value]) => Reflect.get(project[action.type], key) === value,
|
||||
)
|
||||
)
|
||||
return state;
|
||||
return changedPlan(state, {
|
||||
...project,
|
||||
[action.type]: { ...project[action.type], ...action.patch },
|
||||
});
|
||||
}
|
||||
case 'tolerance':
|
||||
return action.value === project.tolerance
|
||||
? state
|
||||
: changedPlan(state, { ...project, tolerance: action.value });
|
||||
case 'addRoom': {
|
||||
if (project.rooms.length >= PROJECT_LIMITS.rooms) return state;
|
||||
const id = nextId(
|
||||
'room',
|
||||
project.rooms.map((room) => room.id),
|
||||
);
|
||||
const next = changedPlan(state, {
|
||||
...project,
|
||||
rooms: [...project.rooms, rectangle(id, `Room ${project.rooms.length + 1}`)],
|
||||
});
|
||||
return {
|
||||
...next,
|
||||
view: {
|
||||
...next.view,
|
||||
roomId: id,
|
||||
collapsedSections: view.collapsedSections.filter((section) => section !== 'measurements'),
|
||||
},
|
||||
};
|
||||
}
|
||||
case 'removeRoom': {
|
||||
if (project.rooms.length <= 1) return state;
|
||||
const rooms = project.rooms.filter((room) => room.id !== action.id);
|
||||
if (rooms.length === project.rooms.length) return state;
|
||||
const next = changedPlan(state, { ...project, rooms });
|
||||
return {
|
||||
...next,
|
||||
view: {
|
||||
...next.view,
|
||||
roomId: rooms.some((room) => room.id === view.roomId) ? view.roomId : rooms[0].id,
|
||||
},
|
||||
};
|
||||
}
|
||||
case 'visitRoom':
|
||||
return project.rooms.some((room) => room.id === action.id)
|
||||
? { ...state, view: { ...view, roomId: action.id, selectedPiece: null } }
|
||||
: state;
|
||||
case 'material':
|
||||
return {
|
||||
...state,
|
||||
view: { ...view, material: action.material, selectedPiece: null, search: '', focus: null },
|
||||
};
|
||||
case 'select':
|
||||
return {
|
||||
...state,
|
||||
view: {
|
||||
...view,
|
||||
selectedPiece: `${action.material}:${action.id}`,
|
||||
material: action.material,
|
||||
roomId: action.roomId ?? view.roomId,
|
||||
},
|
||||
};
|
||||
case 'closePreview':
|
||||
return { ...state, view: { ...view, selectedPiece: null } };
|
||||
case 'search':
|
||||
return {
|
||||
...state,
|
||||
view: {
|
||||
...view,
|
||||
search: action.text,
|
||||
roomId: action.roomId,
|
||||
material: action.material,
|
||||
focus: null,
|
||||
selectedPiece: null,
|
||||
},
|
||||
};
|
||||
case 'focus':
|
||||
return {
|
||||
...state,
|
||||
view: {
|
||||
...view,
|
||||
focus: action.focus,
|
||||
material: action.focus.material,
|
||||
roomId: action.roomId,
|
||||
search: '',
|
||||
selectedPiece: null,
|
||||
},
|
||||
};
|
||||
case 'toggleBoard':
|
||||
return {
|
||||
...state,
|
||||
project: { ...project, completedBoards: toggle(project.completedBoards, action.key) },
|
||||
};
|
||||
case 'toggleSection':
|
||||
return {
|
||||
...state,
|
||||
view: { ...view, collapsedSections: toggle(view.collapsedSections, action.section) },
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue