Add steps to the editor #114
Merged
maxime.batista
merged 16 commits from editor/steps
into master
1 year ago
Loading…
Reference in new issue
There is no content yet.
Delete Branch 'editor/steps'
Deleting a branch is permanent. It CANNOT be undone. Continue?
look at this fancy tree

c5577aae2f
tofcd0a94535
1 year agoset -e
set -xeu
set -e
is already used two lines before (and add a shebang).)
const infoResponse = await infoResponsePromise
const treeResponse = await treeResponsePromise
You may want to do those requests simultaneously:
a8a00ea687
tocc3a3429fd
1 year agocc3a3429fd
toafab2aa55d
1 year agoafab2aa55d
to11bed6278c
1 year ago11bed6278c
toe936aadb76
1 year agoe936aadb76
toa16b813718
1 year agoa16b813718
to45b317dc6d
1 year ago45b317dc6d
todf10eba8d2
1 year agoDesign autonomous components. Too many are tied to the DOM, to the whole tree or to a global state.
Don't blindly think that functional programming imposes perfect immutability, especially with user interfaces.
As React state management becomes more complex when the application grows, it's important to keep the state management and prop drilling as simple as possible.
Think about the data flow in your application, place it in the parent component, and pass it down to the child components as props.
If you need your state to involve nested data structures or partial updates, consider not exposing the state setter directly.
Wrap it in a function that will handle closely related state updates.
useReducer
can be a good fit for this.Components that use those dispatch functions usually have fewer props and may use an alternate data representation when raising updates.
Here's an example with the
StepsTreeNode
component:State and updates are still abstracted from the component in the reducer create upper in the component hierarchy,
but the component collaborates with the parent to add information as the update progresses upwards.
Those days, state updates are often asynchronous, and it can become difficult to reason about the state of the application at a given point.
There are state management libraries that can help with this, but it can also be done by combining React native hooks.
For example, the state may be updated only after an API request has been completed. It could look like this:
The dispatch arguments are adapted when the result is received in order to reflect to update the UI accordingly.
State management is a complex topic, with a lot of pitfalls and not always clean solutions.
Thinking about where events are created, where they are completed, when the state is updated, and how the API is called can help to keep the state management clean and maintainable.
Good luck with that!
# this sed command will replace the included `profile/dev-config-profile.php` to `profile/prod-config-file.php` in the config.php file.
sed -E -i 's/\/\*PROFILE_FILE\*\/\s*".*"/"profiles\/prod-config-profile.php"/' config.php
DRONE_BRANCH_ESCAPED=$(sed -E 's/\//\\\//g' <<< "$DRONE_BRANCH")
sed -E -i "s/const BASE_PATH = .*;/const BASE_PATH = \"\/IQBall\/$DRONE_BRANCH_ESCAPED\/public\";/" profiles/prod-config-profile.php
So, PHP is back? 👀
import { ReactNode, useCallback, useEffect, useRef, useState } from "react"
export interface SlideLayoutProps {
The name of the props interface does not match the component name.
onRightWidthChange: (w: number) => void
}
export default function CurtainLayout({
const handleMouseDown = () => setResizing(true)
slider.addEventListener("mousedown", handleMouseDown)
React exposes a
mouseup
/mousemove
/mousedown
event, no need to manually attach it.rootNode={root}
selectedStepId={selectedStepId}
onAddChildren={onAddChildren}
onRemoveNode={onRemoveNode}
Those two event handlers are often passed around and are closely related. In order to reduce prop drilling and to expose a cleaner API, you could group them and create a dispatcher of various actions over a tree. See the
useReducer
hook.}: StepsTreeContentProps) {
const ref = useRef<HTMLDivElement>(null)
const stepId = getStepName(rootNode, node.id)
Uh, given that function explores in the worst case the whole tree, calling that at every render for each node may not be the best idea in the world. A child component shouldn't also use the root node as it is usually not the root node.
There are multiple way to handle that. The tree component could precompute the index, or the data structure could be adjusted to include some info about the node position. It is a good candidate for a order statistic tree for instance.
<BendableArrow
key={child.id}
area={ref}
startPos={"step-piece-" + stepId}
segments={[
{
next:
"step-piece-" + getStepName(rootNode, child.id),
No need to compute a name twice if its usage is only internal.
isSelected: boolean
onAddButtonClicked?: () => void
onRemoveButtonClicked?: () => void
onSelected: () => void
Differentiate the internal ID and the displayed representation.
<div className="step-piece-actions">
{onAddButtonClicked && (
<AddSvg
onClick={() => onAddButtonClicked()}
Do not create an useless lambda, and avoid any memoization issues.
)}
{onRemoveButtonClicked && (
<RemoveSvg
onClick={() => onRemoveButtonClicked()}
/**
* Spreads the changes to others actions and components, directly or indirectly bound to the origin, implied by the change of the origin's actual state with
* the given newState.
* @returns the new state if it has been updated, or null if no changes were operated
@return
should come after the parameters.return {
...root,
children: root.children.map((c) => addStepNode(c, parent, child)),
This algorithm creates a complete copy of the tree and do not reuse subtree that don't change. It would be nice not to do so.
return guestMode ? <GuestModeEditor /> : <UserModeEditor />
}
function GuestModeEditor() {
Uh no... Abstract only the storage source instead of redefining the logic for every event handler.
if (!response.ok) return null
const { stepId } = await response.json()
const child = { id: stepId, children: [] }
setStepsTree(addStepNode(stepsTree, parent, child))
)
}),
[doDeleteAction, doUpdateAction],
[courtRef, doDeleteAction, doUpdateAction, editorContentCurtainWidth],
#show-steps-button {
user-select: none;
-moz-user-select: none;
-ms-user-select: none;
We no longer live in the past!
beforeAll(login)
test("create tactic", async () => {
Add the test directory in the
tsconfig
file.expect(createTacticResponse.status).toBe(200)
const { id } = await createTacticResponse.json()
const tasks = Array.from({ length: 200 }).map(async () => {
const steps = []
for (const task of tasks) {
steps.push(await task)
}
e7a3cc119a
toc8cc65bf96
1 year agoc8cc65bf96
to165c5ca984
1 year agoWhile not being very idiomatic, the "service" way of managing the states is much better.
#VITE_API_ENDPOINT=https://iqball.maxou.dev/api/dotnet-master
VITE_API_ENDPOINT=http://localhost:5254
VITE_API_ENDPOINT=http://grospc:5254
Be generic.
import React, { ReactNode, useCallback, useRef, useState } from "react"
export interface CurtainLayoutProps {
A curtain is something that progressively reveals something like in a theater, whereas a split layout shares the container width in two and is adjustable.
/>
)}
</div>
<p>{children}</p>
A
p
tag cannot nest anotherp
tag.content.components.filter((c) => c.type !== "phantom") as (
| Player
| CourtObject
)[]
Use a type guard to narrow the array type.
165c5ca984
tob87db24e9e
1 year ago4f6b905f22
into master 1 year agoReviewers
4f6b905f22
.