Add guided onboarding tour for first toolbox creation
All checks were successful
Deploy Sokko G / deploy (push) Successful in 14s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 14s
This commit is contained in:
parent
3007aa1389
commit
4f73e0068b
20 changed files with 470 additions and 20 deletions
|
|
@ -1,5 +1,6 @@
|
|||
// Rôle : affiche les pages toolbox, le panneau latéral et leurs contrôles.
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { GuidedTooltipTour } from "../../components/GuidedTooltipTour.jsx";
|
||||
import { Icon } from "../../components/Icon.jsx";
|
||||
import { ImportButton } from "../../components/ImportButton.jsx";
|
||||
import { StorageQuota } from "../../components/StorageQuota.jsx";
|
||||
|
|
@ -169,10 +170,18 @@ export function ToolboxPage(props) {
|
|||
return <ToolboxView {...props} toolbox={toolbox} toolboxGame={getToolboxGame(toolbox)} embedded={false} />;
|
||||
}
|
||||
|
||||
function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, storageUsage, getModuleData, updateToolbox, updateModuleData, addImageFiles }) {
|
||||
function shouldShowTutorial(toolbox, pendingTutorialToolboxId) {
|
||||
return toolbox?.tutorial === true || pendingTutorialToolboxId === toolbox?.id;
|
||||
}
|
||||
|
||||
function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, storageUsage, getModuleData, updateToolbox, updateModuleData, addImageFiles, pendingTutorialToolboxId = "", setPendingTutorialToolboxId }) {
|
||||
const toolboxId = toolbox.id;
|
||||
const toolboxTutorial = toolbox.tutorial;
|
||||
const moduleColumns = embedded || Number(toolbox.moduleColumns) === 1 ? 1 : 2;
|
||||
const toolboxGameCover = getGameCardCover(toolboxGame);
|
||||
const moduleText = siteContent.toolboxes.modules;
|
||||
const [showTutorial, setShowTutorial] = useState(() => shouldShowTutorial(toolbox, pendingTutorialToolboxId));
|
||||
const currentToolboxIdRef = useRef(toolboxId);
|
||||
const moduleContext = {
|
||||
getModuleData,
|
||||
moduleText,
|
||||
|
|
@ -202,6 +211,15 @@ function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, sto
|
|||
refresh: () => {}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (currentToolboxIdRef.current === toolboxId) {
|
||||
if (shouldShowTutorial(toolbox, pendingTutorialToolboxId)) setShowTutorial(true);
|
||||
return;
|
||||
}
|
||||
currentToolboxIdRef.current = toolboxId;
|
||||
setShowTutorial(shouldShowTutorial(toolbox, pendingTutorialToolboxId));
|
||||
}, [pendingTutorialToolboxId, toolbox, toolboxId, toolboxTutorial]);
|
||||
|
||||
function addModule(type) {
|
||||
if (!type) return;
|
||||
const module = { id: uid("mod"), type };
|
||||
|
|
@ -231,10 +249,10 @@ function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, sto
|
|||
<div>
|
||||
<p className="eyebrow">Toolbox</p>
|
||||
<div className="toolbox-title-row">
|
||||
{!embedded && !toolboxGame && <ToolboxIconPicker toolbox={toolbox} onChange={(icon) => updateToolbox({ ...toolbox, icon })} />}
|
||||
{!embedded && !toolboxGame && <ToolboxIconPicker toolbox={toolbox} onChange={(icon) => updateToolbox({ ...toolbox, icon })} tutorialTarget="icon" />}
|
||||
{!embedded && toolboxGameCover && <ToolboxGameIcon game={toolboxGame} />}
|
||||
{embedded ? <h1>{toolbox.name}</h1> : (
|
||||
<EditableTitle className="toolbox-title" value={toolbox.name} fallback="Nouvelle toolbox" onSave={(name) => name !== toolbox.name && updateToolbox({ ...toolbox, name })} />
|
||||
<EditableTitle className="toolbox-title" value={toolbox.name} fallback="Nouvelle toolbox" tutorialTarget="title" onSave={(name) => name !== toolbox.name && updateToolbox({ ...toolbox, name })} />
|
||||
)}
|
||||
</div>
|
||||
{toolboxGame && (
|
||||
|
|
@ -252,6 +270,25 @@ function ToolboxView({ siteContent, toolbox, toolboxGame, embedded, actions, sto
|
|||
<AddToolControls onAdd={addModule} />
|
||||
</div>
|
||||
</section>
|
||||
{!embedded && showTutorial && (
|
||||
<GuidedTooltipTour
|
||||
steps={siteContent.toolboxes.tutorial?.items || []}
|
||||
labels={{
|
||||
dismiss: siteContent.toolboxes.tutorial?.dismissLabel,
|
||||
next: siteContent.toolboxes.tutorial?.nextLabel,
|
||||
done: siteContent.toolboxes.tutorial?.doneLabel
|
||||
}}
|
||||
targetAttribute="data-toolbox-tutorial-target"
|
||||
onDismiss={() => {
|
||||
if (pendingTutorialToolboxId === toolbox.id) setPendingTutorialToolboxId?.("");
|
||||
setShowTutorial(false);
|
||||
}}
|
||||
onStarted={() => {
|
||||
if (pendingTutorialToolboxId === toolbox.id) setPendingTutorialToolboxId?.("");
|
||||
if (toolbox.tutorial === true) updateToolbox({ ...toolbox, tutorial: false });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!embedded && (
|
||||
<section className="modules-toolbar" aria-label="Options d'affichage des outils">
|
||||
<div className="layout-switch" role="group" aria-label="Mode d'affichage des outils">
|
||||
|
|
@ -359,7 +396,7 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
|
|||
);
|
||||
}
|
||||
|
||||
function EditableTitle({ value, fallback, onSave, className = "module-title" }) {
|
||||
function EditableTitle({ value, fallback, onSave, className = "module-title", tutorialTarget = "" }) {
|
||||
const inlineEdit = useInlineEdit({
|
||||
value,
|
||||
onCommit: (title) => onSave(title || fallback),
|
||||
|
|
@ -370,6 +407,7 @@ function EditableTitle({ value, fallback, onSave, className = "module-title" })
|
|||
<h1
|
||||
{...inlineEdit.getContentEditableProps({
|
||||
className,
|
||||
"data-toolbox-tutorial-target": tutorialTarget || undefined,
|
||||
title: "Cliquer pour renommer"
|
||||
})}
|
||||
>{value}</h1>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue