FormidableLabs / spectacle

A React-based library for creating sleek presentations using JSX syntax that gives you the ability to live demo your code.
https://commerce.nearform.com/open-source/spectacle/
MIT License
9.77k stars 693 forks source link

No Stepper Component? (is it now the use-steps hook?) #1009

Closed sneakers-the-rat closed 3 years ago

sneakers-the-rat commented 3 years ago

Prerequisites

Feel free to delete this section if you have checked off all of the following.

Describe Your Environment

What version of Spectacle are you using? (can be found by running npm list spectacle): 8.0.1

What version of React are you using? (can be found by running npm list react): 17.0.2

What browser are you using? Firefox

What machine are you on? macOS

Describe the Problem

Stepper is advertised on the docs, and is mentioned in the changelog, which links to PR https://github.com/FormidableLabs/spectacle/pull/843 which seems to add a components/stepper.js file, but that never made it to the main branch. Did this functionality get merged into some other object? In any case there's a mismatch between the docs and the code.

Expected behavior: [What you expect to happen]

import { Stepper } from 'spectacle';

then i guess being able to use it...

Actual behavior: [What actually happens]

export 'Stepper' (imported as 'Stepper') was not found in 'spectacle' (possible exports: Appear, Box, CodePane, CodeSpan, Deck, DeckContext, FlexBox, FullScreen, FullSizeImage, Grid, Heading, Image, Link, ListItem, Markdown, MarkdownPreHelper, MarkdownSlide, MarkdownSlideSet, Notes, OrderedList, Progress, Quote, Slide, SlideContext, SpectacleLogo, Table, TableBody, TableCell, TableHeader, TableRow, Text, UnorderedList, defaultTheme, indentNormalizer, isolateNotes, mdxComponentMap, removeNotes)

sneakers-the-rat commented 3 years ago

Or, i guess more generally, what i'm trying to do is step through an animation of an SVG, and i want to hook into the keypress event so that the slide doesn't change but the animation proceeds.

sneakers-the-rat commented 3 years ago

I see: https://github.com/FormidableLabs/spectacle/blob/main/src/hooks/use-steps.js and https://github.com/FormidableLabs/spectacle/blob/main/src/hooks/use-keyboard-controls.js as being potentially relevant, but not sure how to use them? I see notes to expand documentation there, if you point me in a general direction with a basic example I can give it a shot and try and write some docs?

sneakers-the-rat commented 3 years ago

from what i can tell from the PR that removed it, the functionality seems to have moved to hooks/use-steps.js and a lot of the Stepper docs were removed: https://github.com/FormidableLabs/spectacle/pull/980/

Taking a look at components/code-pane.js is useful for understanding how to implement 'sub-steps' within a slide https://github.com/FormidableLabs/spectacle/blob/main/src/components/code-pane.js and so I think I could figure it out from there. I know docs are a continual challenge so not faulting y'all there, but it seems like once you get the hooks documented the package will become a whole hell of a lot more powerful! happy to submit what i come up with as an example if it's useful -- i'll be making a component to step through svg animations by exposing the anime.js API as a list of substeps, if something like that sounds like something that could be an extension.

sneakers-the-rat commented 3 years ago

Ok figured out how to step through an .svg (using anime.js), and generally how to add custom step logic to slides, definitely worth documenting bc you can do stuff like this which is good:

spectacle_stepper

Here's what i've been making, for reference (no roastin' allowed): https://github.com/sneakers-the-rat/infrastructure-presentation/blob/2e23e79d9b65ed583a4bd13f501d226ed3e27673/src/components/svg_animator.js

After exporting the hooks and contexts (see https://github.com/FormidableLabs/spectacle/pull/1019 and https://github.com/sneakers-the-rat/spectacle/commits/export_context ) the way it seems to work is like this --- probably messing something up b/c still not sure intended use, but hey if it works...

useSteps

useSteps seems to add steps to a slide, it takes a stepIndex param but not quite sure what it does, and returns a few props, most important of which is the placeholder. So to add steps to a slide, in some component do something like this:

import { useSteps } from "spectacle";

export default function SvgAnimator({
steps = [],
nSteps,
stepIndex
}){
  const { stepId, isActive, stepNum, placeholder } = useSteps(numberOfSteps, {
    stepIndex,
  });

and then make sure the placeholder is included in your render/return method

return(
  <>
    {placeholder}
    {...other_stuff}
  </>
)

SlideContext

SlideContext seems to have the notion of the current step in it, so you can use it in a component eg. as state or with useEffect like this, eg. with the material ui collapse component in the above gif:

import React, { useState, useEffect } from "react";
import { SlideContext } from "spectacle";
import Collapse from '@material-ui/core/Collapse'

export default function Collapser({
    openStep=0,
    children
}){
const { activeStepIndex, isSlideActive } = React.useContext(SlideContext);

return(
    <Collapse in={activeStepIndex>=openStep}>
        {children}
    </Collapse>
)

A really hacky way of using it is to do something like ^^ with another component that just adds steps to a slide like this:

import { useSteps } from "spectacle";

export default function Stepper(
    {nSteps= 1}){
  const { stepId, isActive, stepNum, placeholder } = useSteps(nSteps);
  return(<>{placeholder}</>)
}

so eg. you could do something like this in an .mdx slide:

---
<Stepper nSteps={3}/>

<Collapser openStep={1}>Hey</Collapser>
<Collapser openStep={2}>What</Collapser>
<Collapser openStep={3}>Up</Collapser>
---
markerikson commented 3 years ago

Yeah, similarly, v5 rendered left/right arrows in a <Controls> component, and I'm not seeing any arrows in a v8 test presentation.

sneakers-the-rat commented 3 years ago

@markerikson for that I think you would use one of these: https://github.com/FormidableLabs/spectacle/blob/86ebff9bfc00f91d68ba8f7130236abb1c9453cb/src/hooks/use-deck-state.js#L87

in the use deck state hook, but it's not exported in the current version (hence PR above) and can't be used by importing from the library using relative paths. if you made a component that used the hook and then had an onClick event that called the callback it would probably work. i respect the fact that this team seems to have rewritten the whole library to use react hooks so just trying to make it easier to PR these things back in

sneakers-the-rat commented 3 years ago

would b cool to have some response from the devs on this bc this is a basic function of the library & trying to contribute in good faith, even happy to write docs if I get some input on intended usage/behavior, but will just fork and diverge otherwise

markerikson commented 3 years ago

I ended up writing my own logic to handle this, using the slide index values and the advanceSlide/regressSlide callbacks inside of DeckContext.

hinok commented 3 years ago

Upgrading from ^6.0.0 is not possible at the moment as with 8.2.0

export 'Stepper' was not found in 'spectacle'
mhink commented 3 years ago

@sneakers-the-rat hi there! Sorry this has taken so long. I actually wrote the majority of the new code, so here's the 411.

Your analysis of the useSteps hook is more or less accurate. Essentially, this is the mechanism which allows you to write components which "participate" in the presentation flow by rendering the placeholder return value in addition to other components. ("placeholder divs" are how the Deck component detects slides, and how Slide components detect steps.)

The idea is to allow exactly the kind of behavior you're looking for without the need to use render props. Here's some example code:

const Rotator = ({
  angle,
  children,
}) => {
  const springStyle = useSpring({
    transform: `rotate(${angle}deg)`
  });

  return (
    <animated.div style={{
      ...springStyle,
      width: '10rem',
      height: '10rem',
      background: 'blue',
    }}>
      {children}
    </animated.div>
  );
};

const ANGLES_FOR_DEMO = [30, 60, 90];

const RotatorDemo = () => {
  const { isActive, step, placeholder } = useSteps(ANGLES_FOR_DEMO.length);

  const angle = isActive ? ANGLES_FOR_DEMO[step] : 0;

  return (
    <>
      <Rotator angle={angle}>
        {isActive ?? <p>`Rotated ${angle}°`</p>}
      </Rotator>
      {placeholder}
    </>
  );
};

Which you'll notice is very similar to what you wrote!


All that being said, there is an actual bug here: the <Appear /> component was intended to replace <Stepper>, but after looking into it a bit it seems like its "multiple step" functionality was removed for some reason. I'm going to open a PR to add that back, and to add additional documentation for the various hooks.

sneakers-the-rat commented 3 years ago

lovely, thank you for getting to this :)