From b64f98015af68bb67d99df2797feafd0ee4e1f39 Mon Sep 17 00:00:00 2001 From: mwritter Date: Tue, 19 Jul 2022 16:40:25 -0400 Subject: [PATCH 01/21] Added CommandBar component for kbar --- examples/js/index.js | 326 +++++++++--------- packages/spectacle/package.json | 1 + .../src/components/command-bar/index.tsx | 49 +++ packages/spectacle/src/index.ts | 1 + pnpm-lock.yaml | 64 +++- 5 files changed, 279 insertions(+), 162 deletions(-) create mode 100644 packages/spectacle/src/components/command-bar/index.tsx diff --git a/examples/js/index.js b/examples/js/index.js index 0ab3a57c5..497ffda50 100644 --- a/examples/js/index.js +++ b/examples/js/index.js @@ -20,7 +20,8 @@ import { MarkdownSlide, MarkdownSlideSet, Notes, - SlideLayout + SlideLayout, + CommandBar } from 'spectacle'; import ReactDOM from 'react-dom'; @@ -72,121 +73,123 @@ const SlideFragments = () => ( ); const Presentation = () => ( - - - - - - - Spectacle supports notes per slide. -
    -
  1. Notes can now be HTML markup!
  2. -
  3. Lists can make it easier to make points.
  4. -
-
-
- - - - ✨Spectacle ✨ - - - A ReactJS Presentation Library - - - Where you can write your decks in JSX, Markdown, or MDX! - - - - - Custom Backgrounds - - - backgroundColor - - - backgroundImage - - - backgroundOpacity - - - backgroundSize - - - backgroundPosition - - - backgroundRepeat - - - - - Animated Elements - - - Elements can animate in! - - - Out of order - - + + + + + + + + Spectacle supports notes per slide. +
    +
  1. Notes can now be HTML markup!
  2. +
  3. Lists can make it easier to make points.
  4. +
+
+
+ + + + ✨Spectacle ✨ + + + A ReactJS Presentation Library + + + Where you can write your decks in JSX, Markdown, or MDX! + + + + + Custom Backgrounds + - Just identify the order with the prop priority! + backgroundColor -
-
-
- - - These - Text - Items - Flex - - - - Single-size Grid Item - - - Double-size Grid Item - - - - {Array(9) - .fill('') - .map((_, index) => ( - - - - ))} - - - - - {` + + backgroundImage + + + backgroundOpacity + + + backgroundSize + + + backgroundPosition + + + backgroundRepeat + + + + + Animated Elements + + + Elements can animate in! + + + Out of order + + + + Just identify the order with the prop{' '} + priority! + + + + + + + These + Text + Items + Flex + + + + Single-size Grid Item + + + Double-size Grid Item + + + + {Array(9) + .fill('') + .map((_, index) => ( + + + + ))} + + + + + {` import { createClient, Provider } from 'urql'; const client = createClient({ url: 'https://0ufyz.sse.codesandbox.io' }); @@ -197,30 +200,30 @@ const Presentation = () => ( ); `} - - {` + + {` public class NoLineNumbers { public static void main(String[] args) { System.out.println("Hello"); } } `} - -
- - This is a slide embedded in a div -
- - {` +
+ + This is a slide embedded in a div + +
+ + {` # This is a Markdown Slide - You can pass props down to all elements on the slide. - Just use the \`componentProps\` prop. `} - - - {` + + + {` # This is also a Markdown Slide It uses the \`animateListItems\` prop. @@ -229,45 +232,46 @@ const Presentation = () => ( - ...will appear... - ...one at a time. `} - - - - - This is a 4x4 Grid - - - - With all the content aligned and justified center. - - - - - It uses Spectacle {''} and{' '} - {''} components. - - - - - - - - - {` +
+ + + + This is a 4x4 Grid + + + + With all the content aligned and justified center. + + + + + It uses Spectacle {''} and{' '} + {''} components. + + + + + + + + + {` # This is the first slide of a Markdown Slide Set --- # This is the second slide of a Markdown Slide Set `} - - -
+ + + + ); const root = ReactDOM.createRoot(document.getElementById('root')); diff --git a/packages/spectacle/package.json b/packages/spectacle/package.json index 9a7b757a5..2bcd0db01 100644 --- a/packages/spectacle/package.json +++ b/packages/spectacle/package.json @@ -41,6 +41,7 @@ "broadcastchannel-polyfill": "^1.0.0", "dedent": "^0.7.0", "history": "^4.9.0", + "kbar": "0.1.0-beta.36", "mdast-builder": "^1.1.1", "mdast-zone": "^4.0.0", "merge-anything": "^3.0.3", diff --git a/packages/spectacle/src/components/command-bar/index.tsx b/packages/spectacle/src/components/command-bar/index.tsx new file mode 100644 index 000000000..b25af7a2b --- /dev/null +++ b/packages/spectacle/src/components/command-bar/index.tsx @@ -0,0 +1,49 @@ +import { ReactNode } from 'react'; +import { + KBarProvider, + KBarPortal, + KBarPositioner, + KBarAnimator, + KBarSearch, + useMatches, + NO_GROUP +} from 'kbar'; + +const actions = [ + { + id: 'blog', + name: 'Blog', + shortcut: ['b'], + keywords: 'writing words', + perform: () => console.log('Bloggen') + }, + { + id: 'contact', + name: 'Contact', + shortcut: ['c'], + keywords: 'email', + perform: () => console.log('Contacten') + } +]; + +const CommandBar = (props: CommandBarProps): JSX.Element => { + const { children } = props; + return ( + + + + + + + + + {children} + + ); +}; + +export type CommandBarProps = { + children: ReactNode; +}; + +export default CommandBar; diff --git a/packages/spectacle/src/index.ts b/packages/spectacle/src/index.ts index 53dd646c8..777297036 100644 --- a/packages/spectacle/src/index.ts +++ b/packages/spectacle/src/index.ts @@ -19,6 +19,7 @@ export { TableHeader, TableBody } from './components/table'; +export { default as CommandBar } from './components/command-bar'; export type { TableProps } from './components/table'; export { FlexBox, Grid, Box } from './components/layout-primitives'; export { Image, FullSizeImage } from './components/image'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 898c89ccd..fbfb270e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -197,6 +197,7 @@ importers: history: ^4.9.0 html-webpack-plugin: ^5.5.0 jest: ^27.3.1 + kbar: 0.1.0-beta.36 mdast-builder: ^1.1.1 mdast-zone: ^4.0.0 merge-anything: ^3.0.3 @@ -233,6 +234,7 @@ importers: broadcastchannel-polyfill: 1.0.1 dedent: 0.7.0 history: 4.10.1 + kbar: 0.1.0-beta.36_biqbaboplfbrettd7655fr4n2y mdast-builder: 1.1.1 mdast-zone: 4.0.1 merge-anything: 3.0.7 @@ -2142,6 +2144,35 @@ packages: fastq: 1.13.0 dev: true + /@reach/observe-rect/1.2.0: + resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==} + dev: false + + /@reach/portal/0.16.2_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-9ur/yxNkuVYTIjAcfi46LdKUvH0uYZPfEp4usWcpt6PIp+WDF57F/5deMe/uGi/B/nfDweQu8VVwuMVrCb97JQ==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + '@reach/utils': 0.16.0_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + tiny-warning: 1.0.3 + tslib: 2.4.0 + dev: false + + /@reach/utils/0.16.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-PCggBet3qaQmwFNcmQ/GqHSefadAFyNCUekq9RrWoaU9hh/S4iaFgf2MBMdM47eQj5i/Bk0Mm07cP/XPFlkN+Q==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + tiny-warning: 1.0.3 + tslib: 2.4.0 + dev: false + /@sinonjs/commons/1.8.3: resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==} dependencies: @@ -3692,6 +3723,10 @@ packages: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} dev: false + /command-score/0.1.2: + resolution: {integrity: sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==} + dev: false + /commander/2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true @@ -4600,6 +4635,10 @@ packages: resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} dev: true + /fast-equals/2.0.4: + resolution: {integrity: sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==} + dev: false + /fast-glob/3.2.11: resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} engines: {node: '>=8.6.0'} @@ -6199,6 +6238,21 @@ packages: object.assign: 4.1.2 dev: true + /kbar/0.1.0-beta.36_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-i5tU7VYkMmxHCoyG5qzkNeU3qViKBz2F0fjqvWWSKsgVABCF3BjxzAH570Mhn3Zy92x3NGZae8emkBpEk7MKgw==} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@reach/portal': 0.16.2_biqbaboplfbrettd7655fr4n2y + command-score: 0.1.2 + fast-equals: 2.0.4 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-virtual: 2.10.4_react@18.2.0 + tiny-invariant: 1.2.0 + dev: false + /kind-of/3.2.2: resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} engines: {node: '>=0.10.0'} @@ -7262,6 +7316,15 @@ packages: refractor: 3.6.0 dev: false + /react-virtual/2.10.4_react@18.2.0: + resolution: {integrity: sha512-Ir6+oPQZTVHfa6+JL9M7cvMILstFZH/H3jqeYeKI4MSUX+rIruVwFC6nGVXw9wqAw8L0Kg2KvfXxI85OvYQdpQ==} + peerDependencies: + react: ^16.6.3 || ^17.0.0 + dependencies: + '@reach/observe-rect': 1.2.0 + react: 18.2.0 + dev: false + /react/18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -8363,7 +8426,6 @@ packages: /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - dev: true /tsutils/3.21.0_typescript@4.7.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} From cf5d0794ad199a364255aa65ab2aa528a524dd23 Mon Sep 17 00:00:00 2001 From: mwritter Date: Thu, 21 Jul 2022 13:48:51 -0400 Subject: [PATCH 02/21] Added Kbar results section --- .../src/components/command-bar/index.tsx | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/spectacle/src/components/command-bar/index.tsx b/packages/spectacle/src/components/command-bar/index.tsx index b25af7a2b..e8482811a 100644 --- a/packages/spectacle/src/components/command-bar/index.tsx +++ b/packages/spectacle/src/components/command-bar/index.tsx @@ -5,6 +5,7 @@ import { KBarPositioner, KBarAnimator, KBarSearch, + KBarResults, useMatches, NO_GROUP } from 'kbar'; @@ -20,7 +21,7 @@ const actions = [ { id: 'contact', name: 'Contact', - shortcut: ['c'], + shortcut: ['c', 'k'], keywords: 'email', perform: () => console.log('Contacten') } @@ -34,6 +35,7 @@ const CommandBar = (props: CommandBarProps): JSX.Element => { + @@ -46,4 +48,27 @@ export type CommandBarProps = { children: ReactNode; }; +function RenderResults() { + const { results } = useMatches(); + + return ( + + typeof item === 'string' ? ( +
{item}
+ ) : ( +
+ {item.name} +
+ ) + } + /> + ); +} + export default CommandBar; From 373d580d4a3e5fc6a92ed093a6599f98a8145be2 Mon Sep 17 00:00:00 2001 From: mwritter Date: Fri, 22 Jul 2022 16:17:16 -0400 Subject: [PATCH 03/21] Kbar init integration --- .npmrc | 1 + .../command-bar/command-bar-actions.tsx | 86 +++++++++++++++++++ .../src/components/command-bar/index.tsx | 63 ++------------ .../components/command-bar/results/index.tsx | 58 +++++++++++++ .../components/command-bar/search/index.tsx | 37 ++++++++ .../spectacle/src/components/deck/deck.tsx | 34 ++++++++ .../src/components/presenter-mode/timer.tsx | 23 +++++ packages/spectacle/src/utils/constants.ts | 1 + pnpm-lock.yaml | 64 +------------- 9 files changed, 246 insertions(+), 121 deletions(-) create mode 100644 .npmrc create mode 100644 packages/spectacle/src/components/command-bar/command-bar-actions.tsx create mode 100644 packages/spectacle/src/components/command-bar/results/index.tsx create mode 100644 packages/spectacle/src/components/command-bar/search/index.tsx diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..fa4e09523 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +strict-peer-dependencies=false \ No newline at end of file diff --git a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx new file mode 100644 index 000000000..04d4ecea8 --- /dev/null +++ b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx @@ -0,0 +1,86 @@ +import { useCallback, useRef } from 'react'; +import { modeKeyForSearchParam, modeSearchParamForKey } from '../deck/modes'; +import { parse as parseQS, stringify as stringifyQS } from 'query-string'; +import { SpectacleMode, SPECTACLE_MODES } from '../../utils/constants'; + +const useCommandBarActions = () => { + const mode = useRef( + modeKeyForSearchParam( + parseQS(location.search, { + parseBooleans: true + }) + ) + ); + + const toggleMode = useCallback( + (newMode: SpectacleMode, senderSlideIndex?: number) => { + let stepIndex: string | number = 0; + let slideIndex: string | number = senderSlideIndex || ''; + const searchParams = parseQS(location.search, { + parseBooleans: true + }); + + if (!slideIndex) { + slideIndex = searchParams.slideIndex as string; + stepIndex = searchParams.stepIndex as string; + } + + if (mode.current === newMode) { + location.search = stringifyQS({ + slideIndex, + stepIndex + }); + return; + } + + mode.current = newMode; + + location.search = stringifyQS({ + slideIndex, + stepIndex, + ...modeSearchParamForKey(newMode) + }); + }, + [mode] + ); + + // TODO: KBar shortcuts don't work with 'mod', 'shift', or 'opt' keys + // If we want to keep the og shortcut for these modes then remove these + // shortcuts + return [ + { + id: 'Presenter Mode', + name: 'Presenter Mode', + shortcut: ['p'], + keywords: 'presenter', + perform: () => toggleMode(SPECTACLE_MODES.PRESENTER_MODE), + section: 'Mode' + }, + { + id: 'Overview Mode', + name: 'Overview Mode', + shortcut: ['o'], + keywords: 'overview', + perform: () => toggleMode(SPECTACLE_MODES.OVERVIEW_MODE), + section: 'Mode' + }, + { + id: 'Print Mode', + name: 'Print Mode', + shortcut: ['r'], + keywords: 'export', + perform: () => toggleMode(SPECTACLE_MODES.PRINT_MODE), + section: 'Mode' + }, + { + id: 'Export Mode', + name: 'Export Mode', + shortcut: ['e'], + keywords: 'export', + perform: () => toggleMode(SPECTACLE_MODES.EXPORT_MODE), + section: 'Mode' + } + ]; +}; + +export default useCommandBarActions; diff --git a/packages/spectacle/src/components/command-bar/index.tsx b/packages/spectacle/src/components/command-bar/index.tsx index e8482811a..633cabd59 100644 --- a/packages/spectacle/src/components/command-bar/index.tsx +++ b/packages/spectacle/src/components/command-bar/index.tsx @@ -1,44 +1,14 @@ import { ReactNode } from 'react'; -import { - KBarProvider, - KBarPortal, - KBarPositioner, - KBarAnimator, - KBarSearch, - KBarResults, - useMatches, - NO_GROUP -} from 'kbar'; - -const actions = [ - { - id: 'blog', - name: 'Blog', - shortcut: ['b'], - keywords: 'writing words', - perform: () => console.log('Bloggen') - }, - { - id: 'contact', - name: 'Contact', - shortcut: ['c', 'k'], - keywords: 'email', - perform: () => console.log('Contacten') - } -]; +import { KBarProvider } from 'kbar'; +import useCommandBarActions from './command-bar-actions'; +import CommandBarSearch from './search'; const CommandBar = (props: CommandBarProps): JSX.Element => { const { children } = props; + const actions = useCommandBarActions(); return ( - - - - - - - - + {children} ); @@ -48,27 +18,4 @@ export type CommandBarProps = { children: ReactNode; }; -function RenderResults() { - const { results } = useMatches(); - - return ( - - typeof item === 'string' ? ( -
{item}
- ) : ( -
- {item.name} -
- ) - } - /> - ); -} - export default CommandBar; diff --git a/packages/spectacle/src/components/command-bar/results/index.tsx b/packages/spectacle/src/components/command-bar/results/index.tsx new file mode 100644 index 000000000..47933ad66 --- /dev/null +++ b/packages/spectacle/src/components/command-bar/results/index.tsx @@ -0,0 +1,58 @@ +import { KBarResults, useMatches } from 'kbar'; +import styled from 'styled-components'; + +interface ResultProps { + active: boolean; +} + +const CommandBarResults = (): JSX.Element => { + const { results } = useMatches(); + + const Result = styled.div` + display: flex; + justify-content: space-between; + background-color: ${(p) => (p.active ? 'lightgrey' : 'white')}; + color: rgb(28 28 29); + padding: 1rem; + `; + + const ResultSection = styled.div` + background-color: white; + padding-left: 1rem; + border-bottom: 1px solid rgba(0 0 0 / 0.1); + font-size: smaller; + margin: 0 1rem; + `; + + const ShortcutKey = styled.span` + padding: 5px 10px; + background: rgba(0 0 0 / 0.1); + border-radius: 4px; + fontsize: 14; + margin-right: 5px; + `; + + const styleShortcut: React.CSSProperties = {}; + + return ( + + typeof item === 'string' ? ( + {item} + ) : ( + + {item.name} + + {item.shortcut?.map((sc) => ( + {sc} + ))} + + + ) + } + /> + ); +}; + +export default CommandBarResults; diff --git a/packages/spectacle/src/components/command-bar/search/index.tsx b/packages/spectacle/src/components/command-bar/search/index.tsx new file mode 100644 index 000000000..5c4cf5397 --- /dev/null +++ b/packages/spectacle/src/components/command-bar/search/index.tsx @@ -0,0 +1,37 @@ +import CommandBarResults from '../results'; +import { KBarPortal, KBarPositioner, KBarAnimator, KBarSearch } from 'kbar'; +import { opacity } from 'styled-system'; +import styled from 'styled-components'; + +const KBarSearchStyled = styled(KBarSearch)` + padding: 12px 16px; + font-size: 16px; + width: 100%; + box-sizing: border-box; + outline: none; + border: none; +`; + +const KBarAnimatorStyled = styled(KBarAnimator)` + max-width: 600px; + width: 100%; + background: white; + border-radius: 8px; + overflow: hidden; + box-shadow: rgb(0 0 0 / 50%) 0px 16px 70px; +`; + +const CommandBarSearch = (): JSX.Element => { + return ( + + + + + + + + + ); +}; + +export default CommandBarSearch; diff --git a/packages/spectacle/src/components/deck/deck.tsx b/packages/spectacle/src/components/deck/deck.tsx index 62e29efd2..d1a50d23f 100644 --- a/packages/spectacle/src/components/deck/deck.tsx +++ b/packages/spectacle/src/components/deck/deck.tsx @@ -38,6 +38,7 @@ import { defaultTransition, SlideTransition } from '../transitions'; import { SwipeEventData } from 'react-swipeable'; import { MarkdownComponentMap } from '../../utils/mdx-component-mapper'; import TemplateWrapper from '../template-wrapper'; +import { useRegisterActions } from 'kbar'; export type DeckContextType = { deckId: string | number; @@ -217,6 +218,39 @@ export const DeckInternal = forwardRef( ] ); + // TODO: this should only be called if 'KBarProvider' is available + // TODO: actions here aren't working as expected in presenterMode + useRegisterActions([ + { + id: 'Next Slide', + name: 'Next Slide', + shortcut: ['k'], + keywords: 'next', + perform: stepForward, + section: 'Slides' + }, + { + id: 'Previous Slide', + name: 'Previous Slide', + shortcut: ['j'], + keywords: 'previous', + perform: stepBackward, + section: 'Slides' + }, + { + id: 'Restart Presentation', + name: 'Restart Presentation', + shortcut: ['p', 'r'], + keywords: 'restart', + perform: () => + skipTo({ + slideIndex: 0, + stepIndex: 0 + }), + section: 'Slides' + } + ]); + useMousetrap( disableInteractivity ? {} diff --git a/packages/spectacle/src/components/presenter-mode/timer.tsx b/packages/spectacle/src/components/presenter-mode/timer.tsx index 36cb77d2f..72fd308b4 100644 --- a/packages/spectacle/src/components/presenter-mode/timer.tsx +++ b/packages/spectacle/src/components/presenter-mode/timer.tsx @@ -4,13 +4,36 @@ import { FlexBox, Box } from '../layout-primitives'; import InternalButton from '../internal-button'; import { useTimer } from '../../utils/use-timer'; import { SYSTEM_FONT } from '../../utils/constants'; +import { useRegisterActions } from 'kbar'; export const Timer = () => { const [timer, setTimer] = useState(0); const [timerStarted, setTimerStarted] = useState(false); const addToTimer = useCallback((v: number) => setTimer((s) => s + v), []); + const toggleTimer = useCallback(() => setTimerStarted((s) => !s), []); useTimer(addToTimer, 1000, timerStarted); const minutes = Math.floor(Math.round(timer) / 60); + + // TODO: this should only be called if 'KBarProvider' is available + useRegisterActions([ + { + id: 'Start/Pause Timer', + name: 'Start/Pause Timer', + shortcut: ['t', '1'], + keywords: 'start pause', + perform: toggleTimer, + section: 'Timer' + }, + { + id: 'Restart Timer', + name: 'Restart Timer', + shortcut: ['t', '2'], + keywords: 'restart', + perform: () => setTimer(0), + section: 'Timer' + } + ]); + return ( diff --git a/packages/spectacle/src/utils/constants.ts b/packages/spectacle/src/utils/constants.ts index 195a1bd24..0c3d6c645 100644 --- a/packages/spectacle/src/utils/constants.ts +++ b/packages/spectacle/src/utils/constants.ts @@ -3,6 +3,7 @@ export const DEFAULT_SLIDE_INDEX = 0; export const SYSTEM_FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Helvetica, sans-serif'; +// TODO: KBar could replace this but modifier keys don't seem to work with KBar export const KEYBOARD_SHORTCUTS = { PRESENTER_MODE: 'mod+shift+p', OVERVIEW_MODE: 'mod+shift+o', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbfb270e6..898c89ccd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -197,7 +197,6 @@ importers: history: ^4.9.0 html-webpack-plugin: ^5.5.0 jest: ^27.3.1 - kbar: 0.1.0-beta.36 mdast-builder: ^1.1.1 mdast-zone: ^4.0.0 merge-anything: ^3.0.3 @@ -234,7 +233,6 @@ importers: broadcastchannel-polyfill: 1.0.1 dedent: 0.7.0 history: 4.10.1 - kbar: 0.1.0-beta.36_biqbaboplfbrettd7655fr4n2y mdast-builder: 1.1.1 mdast-zone: 4.0.1 merge-anything: 3.0.7 @@ -2144,35 +2142,6 @@ packages: fastq: 1.13.0 dev: true - /@reach/observe-rect/1.2.0: - resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==} - dev: false - - /@reach/portal/0.16.2_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-9ur/yxNkuVYTIjAcfi46LdKUvH0uYZPfEp4usWcpt6PIp+WDF57F/5deMe/uGi/B/nfDweQu8VVwuMVrCb97JQ==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - '@reach/utils': 0.16.0_biqbaboplfbrettd7655fr4n2y - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - tiny-warning: 1.0.3 - tslib: 2.4.0 - dev: false - - /@reach/utils/0.16.0_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-PCggBet3qaQmwFNcmQ/GqHSefadAFyNCUekq9RrWoaU9hh/S4iaFgf2MBMdM47eQj5i/Bk0Mm07cP/XPFlkN+Q==} - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - dependencies: - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - tiny-warning: 1.0.3 - tslib: 2.4.0 - dev: false - /@sinonjs/commons/1.8.3: resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==} dependencies: @@ -3723,10 +3692,6 @@ packages: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} dev: false - /command-score/0.1.2: - resolution: {integrity: sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==} - dev: false - /commander/2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true @@ -4635,10 +4600,6 @@ packages: resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} dev: true - /fast-equals/2.0.4: - resolution: {integrity: sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==} - dev: false - /fast-glob/3.2.11: resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} engines: {node: '>=8.6.0'} @@ -6238,21 +6199,6 @@ packages: object.assign: 4.1.2 dev: true - /kbar/0.1.0-beta.36_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-i5tU7VYkMmxHCoyG5qzkNeU3qViKBz2F0fjqvWWSKsgVABCF3BjxzAH570Mhn3Zy92x3NGZae8emkBpEk7MKgw==} - peerDependencies: - react: ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@reach/portal': 0.16.2_biqbaboplfbrettd7655fr4n2y - command-score: 0.1.2 - fast-equals: 2.0.4 - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - react-virtual: 2.10.4_react@18.2.0 - tiny-invariant: 1.2.0 - dev: false - /kind-of/3.2.2: resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} engines: {node: '>=0.10.0'} @@ -7316,15 +7262,6 @@ packages: refractor: 3.6.0 dev: false - /react-virtual/2.10.4_react@18.2.0: - resolution: {integrity: sha512-Ir6+oPQZTVHfa6+JL9M7cvMILstFZH/H3jqeYeKI4MSUX+rIruVwFC6nGVXw9wqAw8L0Kg2KvfXxI85OvYQdpQ==} - peerDependencies: - react: ^16.6.3 || ^17.0.0 - dependencies: - '@reach/observe-rect': 1.2.0 - react: 18.2.0 - dev: false - /react/18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -8426,6 +8363,7 @@ packages: /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + dev: true /tsutils/3.21.0_typescript@4.7.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} From 0f57f7c9a6b71c451615fc4d9bdc6d0b9b766db4 Mon Sep 17 00:00:00 2001 From: mwritter Date: Mon, 25 Jul 2022 16:10:22 -0400 Subject: [PATCH 04/21] Kbar clean up --- examples/js/index.js | 326 +++++++++--------- .../command-bar/command-bar-actions.tsx | 73 +--- .../components/command-bar/results/index.tsx | 28 +- .../components/command-bar/search/index.tsx | 1 - .../spectacle/src/components/deck/deck.tsx | 69 ++-- .../src/components/deck/default-deck.tsx | 18 +- .../spectacle/src/components/deck/index.tsx | 118 +++---- .../spectacle/src/components/deck/modes.ts | 39 --- .../src/components/presenter-mode/timer.tsx | 4 +- packages/spectacle/src/hooks/use-modes.ts | 88 +++++ packages/spectacle/src/utils/constants.ts | 13 + 11 files changed, 384 insertions(+), 393 deletions(-) delete mode 100644 packages/spectacle/src/components/deck/modes.ts create mode 100644 packages/spectacle/src/hooks/use-modes.ts diff --git a/examples/js/index.js b/examples/js/index.js index 497ffda50..0ab3a57c5 100644 --- a/examples/js/index.js +++ b/examples/js/index.js @@ -20,8 +20,7 @@ import { MarkdownSlide, MarkdownSlideSet, Notes, - SlideLayout, - CommandBar + SlideLayout } from 'spectacle'; import ReactDOM from 'react-dom'; @@ -73,123 +72,121 @@ const SlideFragments = () => ( ); const Presentation = () => ( - - - - - - - - Spectacle supports notes per slide. -
    -
  1. Notes can now be HTML markup!
  2. -
  3. Lists can make it easier to make points.
  4. -
-
-
- - - - ✨Spectacle ✨ - - - A ReactJS Presentation Library - - - Where you can write your decks in JSX, Markdown, or MDX! - - - - - Custom Backgrounds - - - backgroundColor - - - backgroundImage - - - backgroundOpacity - - - backgroundSize - - - backgroundPosition - + + + + + + + Spectacle supports notes per slide. +
    +
  1. Notes can now be HTML markup!
  2. +
  3. Lists can make it easier to make points.
  4. +
+
+
+ + + + ✨Spectacle ✨ + + + A ReactJS Presentation Library + + + Where you can write your decks in JSX, Markdown, or MDX! + + + + + Custom Backgrounds + + + backgroundColor + + + backgroundImage + + + backgroundOpacity + + + backgroundSize + + + backgroundPosition + + + backgroundRepeat + + + + + Animated Elements + + + Elements can animate in! + + + Out of order + + - backgroundRepeat + Just identify the order with the prop priority! -
-
- - Animated Elements - - - Elements can animate in! - - - Out of order - - - - Just identify the order with the prop{' '} - priority! - - - - - - - These - Text - Items - Flex - - - - Single-size Grid Item - - - Double-size Grid Item - - - - {Array(9) - .fill('') - .map((_, index) => ( - - - - ))} - - - - - {` + + + + + + These + Text + Items + Flex + + + + Single-size Grid Item + + + Double-size Grid Item + + + + {Array(9) + .fill('') + .map((_, index) => ( + + + + ))} + + + + + {` import { createClient, Provider } from 'urql'; const client = createClient({ url: 'https://0ufyz.sse.codesandbox.io' }); @@ -200,30 +197,30 @@ const Presentation = () => ( ); `} - - {` + + {` public class NoLineNumbers { public static void main(String[] args) { System.out.println("Hello"); } } `} + +
+ + This is a slide embedded in a div -
- - This is a slide embedded in a div - -
- - {` +
+ + {` # This is a Markdown Slide - You can pass props down to all elements on the slide. - Just use the \`componentProps\` prop. `} - - - {` + + + {` # This is also a Markdown Slide It uses the \`animateListItems\` prop. @@ -232,46 +229,45 @@ const Presentation = () => ( - ...will appear... - ...one at a time. `} - - - - - This is a 4x4 Grid - - - - With all the content aligned and justified center. - - - - - It uses Spectacle {''} and{' '} - {''} components. - - - - - - - - - {` + + + + + This is a 4x4 Grid + + + + With all the content aligned and justified center. + + + + + It uses Spectacle {''} and{' '} + {''} components. + + + + + + + + + {` # This is the first slide of a Markdown Slide Set --- # This is the second slide of a Markdown Slide Set `} - - -
-
+ + + ); const root = ReactDOM.createRoot(document.getElementById('root')); diff --git a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx index 04d4ecea8..8f288f166 100644 --- a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx +++ b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx @@ -1,83 +1,40 @@ -import { useCallback, useRef } from 'react'; -import { modeKeyForSearchParam, modeSearchParamForKey } from '../deck/modes'; -import { parse as parseQS, stringify as stringifyQS } from 'query-string'; -import { SpectacleMode, SPECTACLE_MODES } from '../../utils/constants'; +import { KEYBOARD_SHORTCUTS, SPECTACLE_MODES } from '../../utils/constants'; +import useModes from '../../hooks/use-modes'; const useCommandBarActions = () => { - const mode = useRef( - modeKeyForSearchParam( - parseQS(location.search, { - parseBooleans: true - }) - ) - ); + const { toggleMode } = useModes(); - const toggleMode = useCallback( - (newMode: SpectacleMode, senderSlideIndex?: number) => { - let stepIndex: string | number = 0; - let slideIndex: string | number = senderSlideIndex || ''; - const searchParams = parseQS(location.search, { - parseBooleans: true - }); - - if (!slideIndex) { - slideIndex = searchParams.slideIndex as string; - stepIndex = searchParams.stepIndex as string; - } - - if (mode.current === newMode) { - location.search = stringifyQS({ - slideIndex, - stepIndex - }); - return; - } - - mode.current = newMode; - - location.search = stringifyQS({ - slideIndex, - stepIndex, - ...modeSearchParamForKey(newMode) - }); - }, - [mode] - ); - - // TODO: KBar shortcuts don't work with 'mod', 'shift', or 'opt' keys - // If we want to keep the og shortcut for these modes then remove these - // shortcuts return [ { - id: 'Presenter Mode', + id: SPECTACLE_MODES.PRESENTER_MODE, name: 'Presenter Mode', - shortcut: ['p'], keywords: 'presenter', - perform: () => toggleMode(SPECTACLE_MODES.PRESENTER_MODE), + internal_shortcut: KEYBOARD_SHORTCUTS.PRESENTER_MODE, + perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRESENTER_MODE }), section: 'Mode' }, { - id: 'Overview Mode', + id: SPECTACLE_MODES.OVERVIEW_MODE, name: 'Overview Mode', - shortcut: ['o'], keywords: 'overview', - perform: () => toggleMode(SPECTACLE_MODES.OVERVIEW_MODE), + internal_shortcut: KEYBOARD_SHORTCUTS.OVERVIEW_MODE, + perform: () => toggleMode({ newMode: SPECTACLE_MODES.OVERVIEW_MODE }), section: 'Mode' }, { - id: 'Print Mode', + id: SPECTACLE_MODES.PRINT_MODE, name: 'Print Mode', - shortcut: ['r'], keywords: 'export', - perform: () => toggleMode(SPECTACLE_MODES.PRINT_MODE), + internal_shortcut: KEYBOARD_SHORTCUTS.PRINT_MODE, + perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRINT_MODE }), section: 'Mode' }, { - id: 'Export Mode', + id: SPECTACLE_MODES.EXPORT_MODE, name: 'Export Mode', - shortcut: ['e'], keywords: 'export', - perform: () => toggleMode(SPECTACLE_MODES.EXPORT_MODE), + internal_shortcut: KEYBOARD_SHORTCUTS.EXPORT_MODE, + perform: () => toggleMode({ newMode: SPECTACLE_MODES.EXPORT_MODE }), section: 'Mode' } ]; diff --git a/packages/spectacle/src/components/command-bar/results/index.tsx b/packages/spectacle/src/components/command-bar/results/index.tsx index 47933ad66..b8717d124 100644 --- a/packages/spectacle/src/components/command-bar/results/index.tsx +++ b/packages/spectacle/src/components/command-bar/results/index.tsx @@ -1,4 +1,5 @@ -import { KBarResults, useMatches } from 'kbar'; +import { ActionImpl, KBarResults, useMatches } from 'kbar'; +import { useCallback } from 'react'; import styled from 'styled-components'; interface ResultProps { @@ -8,7 +9,7 @@ interface ResultProps { const CommandBarResults = (): JSX.Element => { const { results } = useMatches(); - const Result = styled.div` + const ResultCommand = styled.div` display: flex; justify-content: space-between; background-color: ${(p) => (p.active ? 'lightgrey' : 'white')}; @@ -16,7 +17,7 @@ const CommandBarResults = (): JSX.Element => { padding: 1rem; `; - const ResultSection = styled.div` + const ResultSectionHeader = styled.div` background-color: white; padding-left: 1rem; border-bottom: 1px solid rgba(0 0 0 / 0.1); @@ -24,7 +25,7 @@ const CommandBarResults = (): JSX.Element => { margin: 0 1rem; `; - const ShortcutKey = styled.span` + const ResultShortcutKey = styled.span` padding: 5px 10px; background: rgba(0 0 0 / 0.1); border-radius: 4px; @@ -32,27 +33,34 @@ const CommandBarResults = (): JSX.Element => { margin-right: 5px; `; - const styleShortcut: React.CSSProperties = {}; + // TODO: Use platform icons for 'mod', 'shift', and 'alt' keys + const getShortcut = useCallback((item: ActionImpl & InteralCommand) => { + return item.internal_shortcut?.split('+') ?? item.shortcut ?? []; + }, []); return ( typeof item === 'string' ? ( - {item} + {item} ) : ( - + {item.name} - {item.shortcut?.map((sc) => ( - {sc} + {getShortcut(item)?.map((sc) => ( + {sc} ))} - + ) } /> ); }; +type InteralCommand = ActionImpl & { + internal_shortcut?: string; +}; + export default CommandBarResults; diff --git a/packages/spectacle/src/components/command-bar/search/index.tsx b/packages/spectacle/src/components/command-bar/search/index.tsx index 5c4cf5397..5794300bf 100644 --- a/packages/spectacle/src/components/command-bar/search/index.tsx +++ b/packages/spectacle/src/components/command-bar/search/index.tsx @@ -1,6 +1,5 @@ import CommandBarResults from '../results'; import { KBarPortal, KBarPositioner, KBarAnimator, KBarSearch } from 'kbar'; -import { opacity } from 'styled-system'; import styled from 'styled-components'; const KBarSearchStyled = styled(KBarSearch)` diff --git a/packages/spectacle/src/components/deck/deck.tsx b/packages/spectacle/src/components/deck/deck.tsx index d1a50d23f..35c16161f 100644 --- a/packages/spectacle/src/components/deck/deck.tsx +++ b/packages/spectacle/src/components/deck/deck.tsx @@ -67,6 +67,7 @@ export type DeckContextType = { }; skipTo(options: { slideIndex: number; stepIndex: number }): void; stepForward(): void; + stepBackward(): void; advanceSlide(): void; regressSlide(): void; commitTransition(newView?: { stepIndex: number }): void; @@ -218,39 +219,40 @@ export const DeckInternal = forwardRef( ] ); - // TODO: this should only be called if 'KBarProvider' is available - // TODO: actions here aren't working as expected in presenterMode - useRegisterActions([ - { - id: 'Next Slide', - name: 'Next Slide', - shortcut: ['k'], - keywords: 'next', - perform: stepForward, - section: 'Slides' - }, - { - id: 'Previous Slide', - name: 'Previous Slide', - shortcut: ['j'], - keywords: 'previous', - perform: stepBackward, - section: 'Slides' - }, - { - id: 'Restart Presentation', - name: 'Restart Presentation', - shortcut: ['p', 'r'], - keywords: 'restart', - perform: () => - skipTo({ - slideIndex: 0, - stepIndex: 0 - }), - section: 'Slides' - } - ]); - + useRegisterActions( + !disableInteractivity + ? [ + { + id: 'Next Slide', + name: 'Next Slide', + shortcut: ['k'], + keywords: 'next', + perform: () => stepForward(), + section: 'Slides' + }, + { + id: 'Previous Slide', + name: 'Previous Slide', + shortcut: ['j'], + keywords: 'previous', + perform: () => stepBackward(), + section: 'Slides' + }, + { + id: 'Restart Presentation', + name: 'Restart Presentation', + shortcut: ['p', 'r'], + keywords: 'restart', + perform: () => + skipTo({ + slideIndex: 0, + stepIndex: 0 + }), + section: 'Slides' + } + ] + : [] + ); useMousetrap( disableInteractivity ? {} @@ -475,6 +477,7 @@ export const DeckInternal = forwardRef( }, skipTo, stepForward, + stepBackward, advanceSlide, regressSlide, commitTransition, diff --git a/packages/spectacle/src/components/deck/default-deck.tsx b/packages/spectacle/src/components/deck/default-deck.tsx index 235f1f43b..489bef631 100644 --- a/packages/spectacle/src/components/deck/default-deck.tsx +++ b/packages/spectacle/src/components/deck/default-deck.tsx @@ -5,7 +5,7 @@ import useMousetrap from '../../hooks/use-mousetrap'; import { KEYBOARD_SHORTCUTS, SPECTACLE_MODES, - SpectacleMode + ToggleModeArgs } from '../../utils/constants'; /** @@ -51,7 +51,9 @@ const DefaultDeck = (props: DefaultDeckProps): JSX.Element => { stepIndex: 0 }), [KEYBOARD_SHORTCUTS.SELECT_SLIDE_OVERVIEW_MODE]: (e) => - toggleMode(e, SPECTACLE_MODES.DEFAULT_MODE) + toggleMode({ + newMode: SPECTACLE_MODES.DEFAULT_MODE + }) } : {}, [] @@ -62,7 +64,11 @@ const DefaultDeck = (props: DefaultDeckProps): JSX.Element => { >( (e, slideIndex) => { if (overviewMode) { - toggleMode(e, SPECTACLE_MODES.DEFAULT_MODE, +slideIndex); + toggleMode({ + e, + newMode: SPECTACLE_MODES.DEFAULT_MODE, + senderSlideIndex: +slideIndex + }); } }, [overviewMode, toggleMode] @@ -98,11 +104,7 @@ const DefaultDeck = (props: DefaultDeckProps): JSX.Element => { export default DefaultDeck; type DefaultDeckProps = DeckProps & { - toggleMode( - e: unknown, - newMode: SpectacleMode, - senderSlideIndex?: number - ): void; + toggleMode(args: ToggleModeArgs): void; overviewMode?: boolean; printMode?: boolean; exportMode?: boolean; diff --git a/packages/spectacle/src/components/deck/index.tsx b/packages/spectacle/src/components/deck/index.tsx index 6a1e41ce3..700be5d70 100644 --- a/packages/spectacle/src/components/deck/index.tsx +++ b/packages/spectacle/src/components/deck/index.tsx @@ -1,98 +1,62 @@ -import { Fragment, useCallback, useRef } from 'react'; -import { parse as parseQS, stringify as stringifyQS } from 'query-string'; +import { Fragment } from 'react'; import DefaultDeck from './default-deck'; import PresenterMode from '../presenter-mode'; import PrintMode from '../print-mode'; import useMousetrap from '../../hooks/use-mousetrap'; -import { - KEYBOARD_SHORTCUTS, - SPECTACLE_MODES, - SpectacleMode -} from '../../utils/constants'; -import { modeKeyForSearchParam, modeSearchParamForKey } from './modes'; +import { KEYBOARD_SHORTCUTS, SPECTACLE_MODES } from '../../utils/constants'; import { DeckProps } from './deck'; +import useModes from '../../hooks/use-modes'; +import CommandBar from '../command-bar'; const SpectacleDeck = (props: DeckProps): JSX.Element => { - const mode = useRef( - modeKeyForSearchParam( - parseQS(location.search, { - parseBooleans: true - }) - ) - ); - - const toggleMode = useCallback( - (e: Event, newMode: SpectacleMode, senderSlideIndex?: number) => { - e?.preventDefault(); - - let stepIndex: string | number = 0; - let slideIndex: string | number = senderSlideIndex || ''; - const searchParams = parseQS(location.search, { - parseBooleans: true - }); - - if (!slideIndex) { - slideIndex = searchParams.slideIndex as string; - stepIndex = searchParams.stepIndex as string; - } - - if (mode.current === newMode) { - location.search = stringifyQS({ - slideIndex, - stepIndex - }); - return; - } - - mode.current = newMode; - - location.search = stringifyQS({ - slideIndex, - stepIndex, - ...modeSearchParamForKey(newMode) - }); - }, - [mode] - ); + const { toggleMode, currentMode } = useModes(); useMousetrap( { [KEYBOARD_SHORTCUTS.PRESENTER_MODE]: (e) => - e && toggleMode(e, SPECTACLE_MODES.PRESENTER_MODE), + e && toggleMode({ e, newMode: SPECTACLE_MODES.PRESENTER_MODE }), [KEYBOARD_SHORTCUTS.PRINT_MODE]: (e) => - e && toggleMode(e, SPECTACLE_MODES.PRINT_MODE), + e && toggleMode({ e, newMode: SPECTACLE_MODES.PRINT_MODE }), [KEYBOARD_SHORTCUTS.EXPORT_MODE]: (e) => - e && toggleMode(e, SPECTACLE_MODES.EXPORT_MODE), + e && toggleMode({ e, newMode: SPECTACLE_MODES.EXPORT_MODE }), [KEYBOARD_SHORTCUTS.OVERVIEW_MODE]: (e) => - e && toggleMode(e, SPECTACLE_MODES.OVERVIEW_MODE) + e && toggleMode({ e, newMode: SPECTACLE_MODES.OVERVIEW_MODE }) }, [] ); - switch (mode.current) { - case SPECTACLE_MODES.DEFAULT_MODE: - return ; - - case SPECTACLE_MODES.PRESENTER_MODE: - return ; - - /** - * Print mode and export mode are identical except for the theme - * that is used. Print mode uses the print theme which is usually - * monotone and export mode uses the default theme. - */ - case SPECTACLE_MODES.PRINT_MODE: - return ; - - case SPECTACLE_MODES.EXPORT_MODE: - return ; - - case SPECTACLE_MODES.OVERVIEW_MODE: - return ; - - default: - return ; - } + const View = () => { + switch (currentMode) { + case SPECTACLE_MODES.DEFAULT_MODE: + return ; + + case SPECTACLE_MODES.PRESENTER_MODE: + return ; + + /** + * Print mode and export mode are identical except for the theme + * that is used. Print mode uses the print theme which is usually + * monotone and export mode uses the default theme. + */ + case SPECTACLE_MODES.PRINT_MODE: + return ; + + case SPECTACLE_MODES.EXPORT_MODE: + return ; + + case SPECTACLE_MODES.OVERVIEW_MODE: + return ; + + default: + return ; + } + }; + + return ( + + + + ); }; export default SpectacleDeck; diff --git a/packages/spectacle/src/components/deck/modes.ts b/packages/spectacle/src/components/deck/modes.ts deleted file mode 100644 index 6665b31fb..000000000 --- a/packages/spectacle/src/components/deck/modes.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { SPECTACLE_MODES, SpectacleMode } from '../../utils/constants'; - -type ModeSearchParams = { - presenterMode?: boolean; - overviewMode?: boolean; - printMode?: boolean; - exportMode?: boolean; -}; - -export function modeSearchParamForKey(key: SpectacleMode): ModeSearchParams { - if (key === SPECTACLE_MODES.PRESENTER_MODE) { - return { presenterMode: true }; - } else if (key === SPECTACLE_MODES.OVERVIEW_MODE) { - return { overviewMode: true }; - } else if (key === SPECTACLE_MODES.PRINT_MODE) { - return { printMode: true }; - } else if (key === SPECTACLE_MODES.EXPORT_MODE) { - return { exportMode: true }; - } - return {}; -} - -export function modeKeyForSearchParam({ - presenterMode, - overviewMode, - printMode, - exportMode -}: ModeSearchParams) { - if (presenterMode) { - return SPECTACLE_MODES.PRESENTER_MODE; - } else if (overviewMode) { - return SPECTACLE_MODES.OVERVIEW_MODE; - } else if (printMode) { - return SPECTACLE_MODES.PRINT_MODE; - } else if (exportMode) { - return SPECTACLE_MODES.EXPORT_MODE; - } - return SPECTACLE_MODES.DEFAULT_MODE; -} diff --git a/packages/spectacle/src/components/presenter-mode/timer.tsx b/packages/spectacle/src/components/presenter-mode/timer.tsx index 72fd308b4..829859629 100644 --- a/packages/spectacle/src/components/presenter-mode/timer.tsx +++ b/packages/spectacle/src/components/presenter-mode/timer.tsx @@ -11,10 +11,10 @@ export const Timer = () => { const [timerStarted, setTimerStarted] = useState(false); const addToTimer = useCallback((v: number) => setTimer((s) => s + v), []); const toggleTimer = useCallback(() => setTimerStarted((s) => !s), []); + const resetTimer = useCallback(() => setTimer(0), []); useTimer(addToTimer, 1000, timerStarted); const minutes = Math.floor(Math.round(timer) / 60); - // TODO: this should only be called if 'KBarProvider' is available useRegisterActions([ { id: 'Start/Pause Timer', @@ -29,7 +29,7 @@ export const Timer = () => { name: 'Restart Timer', shortcut: ['t', '2'], keywords: 'restart', - perform: () => setTimer(0), + perform: resetTimer, section: 'Timer' } ]); diff --git a/packages/spectacle/src/hooks/use-modes.ts b/packages/spectacle/src/hooks/use-modes.ts new file mode 100644 index 000000000..3cfa9f9c5 --- /dev/null +++ b/packages/spectacle/src/hooks/use-modes.ts @@ -0,0 +1,88 @@ +import { useCallback, useRef } from 'react'; +import { parse as parseQS, stringify as stringifyQS } from 'query-string'; +import { + SPECTACLE_MODES, + SpectacleMode, + ToggleModeArgs, + ModeSearchParams +} from '../utils/constants'; + +const useModes = () => { + const mode = useRef( + modeKeyForSearchParam( + parseQS(location.search, { + parseBooleans: true + }) + ) + ); + + const toggleMode = useCallback( + (args: ToggleModeArgs) => { + const { newMode, senderSlideIndex, e } = args; + e?.preventDefault(); + + let stepIndex: string | number = 0; + let slideIndex: string | number = senderSlideIndex || ''; + const searchParams = parseQS(location.search, { + parseBooleans: true + }); + + if (!slideIndex) { + slideIndex = searchParams.slideIndex as string; + stepIndex = searchParams.stepIndex as string; + } + + if (mode.current === newMode) { + location.search = stringifyQS({ + slideIndex, + stepIndex + }); + return; + } + + mode.current = newMode; + + location.search = stringifyQS({ + slideIndex, + stepIndex, + ...modeSearchParamForKey(newMode) + }); + }, + [mode] + ); + + return { toggleMode, currentMode: mode.current }; +}; + +function modeSearchParamForKey(key: SpectacleMode): ModeSearchParams { + if (key === SPECTACLE_MODES.PRESENTER_MODE) { + return { presenterMode: true }; + } else if (key === SPECTACLE_MODES.OVERVIEW_MODE) { + return { overviewMode: true }; + } else if (key === SPECTACLE_MODES.PRINT_MODE) { + return { printMode: true }; + } else if (key === SPECTACLE_MODES.EXPORT_MODE) { + return { exportMode: true }; + } + return {}; +} + +function modeKeyForSearchParam({ + presenterMode, + overviewMode, + printMode, + exportMode +}: ModeSearchParams) { + if (presenterMode) { + return SPECTACLE_MODES.PRESENTER_MODE; + } else if (overviewMode) { + return SPECTACLE_MODES.OVERVIEW_MODE; + } else if (printMode) { + return SPECTACLE_MODES.PRINT_MODE; + } else if (exportMode) { + return SPECTACLE_MODES.EXPORT_MODE; + } + return SPECTACLE_MODES.DEFAULT_MODE; +} + +export default useModes; diff --git a/packages/spectacle/src/utils/constants.ts b/packages/spectacle/src/utils/constants.ts index 0c3d6c645..033048dde 100644 --- a/packages/spectacle/src/utils/constants.ts +++ b/packages/spectacle/src/utils/constants.ts @@ -23,3 +23,16 @@ export const SPECTACLE_MODES = { } as const; type ValuesOf = T[keyof T]; export type SpectacleMode = ValuesOf; + +export type ModeSearchParams = { + presenterMode?: boolean; + overviewMode?: boolean; + printMode?: boolean; + exportMode?: boolean; +}; + +export type ToggleModeArgs = { + newMode: SpectacleMode; + senderSlideIndex?: number; + e?: Event; +}; From f80cc48a2dbf552ba23dacb57700d3feb1618400 Mon Sep 17 00:00:00 2001 From: mwritter Date: Mon, 25 Jul 2022 17:05:18 -0400 Subject: [PATCH 05/21] Kbar clean up --- .../command-bar/command-bar-actions.tsx | 8 ++++---- .../src/components/command-bar/results/index.tsx | 15 ++++----------- packages/spectacle/src/components/deck/deck.tsx | 3 --- .../src/components/presenter-mode/timer.tsx | 6 ++---- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx index 8f288f166..895ee5d4f 100644 --- a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx +++ b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx @@ -9,7 +9,7 @@ const useCommandBarActions = () => { id: SPECTACLE_MODES.PRESENTER_MODE, name: 'Presenter Mode', keywords: 'presenter', - internal_shortcut: KEYBOARD_SHORTCUTS.PRESENTER_MODE, + shortcut: KEYBOARD_SHORTCUTS.PRESENTER_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRESENTER_MODE }), section: 'Mode' }, @@ -17,7 +17,7 @@ const useCommandBarActions = () => { id: SPECTACLE_MODES.OVERVIEW_MODE, name: 'Overview Mode', keywords: 'overview', - internal_shortcut: KEYBOARD_SHORTCUTS.OVERVIEW_MODE, + shortcut: KEYBOARD_SHORTCUTS.OVERVIEW_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.OVERVIEW_MODE }), section: 'Mode' }, @@ -25,7 +25,7 @@ const useCommandBarActions = () => { id: SPECTACLE_MODES.PRINT_MODE, name: 'Print Mode', keywords: 'export', - internal_shortcut: KEYBOARD_SHORTCUTS.PRINT_MODE, + shortcut: KEYBOARD_SHORTCUTS.PRINT_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRINT_MODE }), section: 'Mode' }, @@ -33,7 +33,7 @@ const useCommandBarActions = () => { id: SPECTACLE_MODES.EXPORT_MODE, name: 'Export Mode', keywords: 'export', - internal_shortcut: KEYBOARD_SHORTCUTS.EXPORT_MODE, + shortcut: KEYBOARD_SHORTCUTS.EXPORT_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.EXPORT_MODE }), section: 'Mode' } diff --git a/packages/spectacle/src/components/command-bar/results/index.tsx b/packages/spectacle/src/components/command-bar/results/index.tsx index b8717d124..9ac5ed0b6 100644 --- a/packages/spectacle/src/components/command-bar/results/index.tsx +++ b/packages/spectacle/src/components/command-bar/results/index.tsx @@ -1,5 +1,4 @@ import { ActionImpl, KBarResults, useMatches } from 'kbar'; -import { useCallback } from 'react'; import styled from 'styled-components'; interface ResultProps { @@ -34,10 +33,6 @@ const CommandBarResults = (): JSX.Element => { `; // TODO: Use platform icons for 'mod', 'shift', and 'alt' keys - const getShortcut = useCallback((item: ActionImpl & InteralCommand) => { - return item.internal_shortcut?.split('+') ?? item.shortcut ?? []; - }, []); - return ( { {item.name} - {getShortcut(item)?.map((sc) => ( - {sc} + {item.shortcut?.map((sc) => ( + + {sc} + ))} @@ -59,8 +56,4 @@ const CommandBarResults = (): JSX.Element => { ); }; -type InteralCommand = ActionImpl & { - internal_shortcut?: string; -}; - export default CommandBarResults; diff --git a/packages/spectacle/src/components/deck/deck.tsx b/packages/spectacle/src/components/deck/deck.tsx index 35c16161f..85274e89e 100644 --- a/packages/spectacle/src/components/deck/deck.tsx +++ b/packages/spectacle/src/components/deck/deck.tsx @@ -225,7 +225,6 @@ export const DeckInternal = forwardRef( { id: 'Next Slide', name: 'Next Slide', - shortcut: ['k'], keywords: 'next', perform: () => stepForward(), section: 'Slides' @@ -233,7 +232,6 @@ export const DeckInternal = forwardRef( { id: 'Previous Slide', name: 'Previous Slide', - shortcut: ['j'], keywords: 'previous', perform: () => stepBackward(), section: 'Slides' @@ -241,7 +239,6 @@ export const DeckInternal = forwardRef( { id: 'Restart Presentation', name: 'Restart Presentation', - shortcut: ['p', 'r'], keywords: 'restart', perform: () => skipTo({ diff --git a/packages/spectacle/src/components/presenter-mode/timer.tsx b/packages/spectacle/src/components/presenter-mode/timer.tsx index 829859629..1b972c626 100644 --- a/packages/spectacle/src/components/presenter-mode/timer.tsx +++ b/packages/spectacle/src/components/presenter-mode/timer.tsx @@ -19,7 +19,6 @@ export const Timer = () => { { id: 'Start/Pause Timer', name: 'Start/Pause Timer', - shortcut: ['t', '1'], keywords: 'start pause', perform: toggleTimer, section: 'Timer' @@ -27,7 +26,6 @@ export const Timer = () => { { id: 'Restart Timer', name: 'Restart Timer', - shortcut: ['t', '2'], keywords: 'restart', perform: resetTimer, section: 'Timer' @@ -46,11 +44,11 @@ export const Timer = () => { Math.round(timer) - minutes * 60 ).padStart(2, '0')}`}
- setTimerStarted((s) => !s)}> + {timerStarted ? 'Stop Timer' : 'Start Timer'} - setTimer(0)}>Reset + Reset
); }; From 253244c14492ee065d0a0f5f57f6dba11a2009c8 Mon Sep 17 00:00:00 2001 From: mwritter Date: Mon, 25 Jul 2022 17:08:43 -0400 Subject: [PATCH 06/21] Kbar clean up --- packages/spectacle/src/components/deck/default-deck.tsx | 4 ++-- packages/spectacle/src/hooks/use-modes.ts | 4 ++-- packages/spectacle/src/utils/constants.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/spectacle/src/components/deck/default-deck.tsx b/packages/spectacle/src/components/deck/default-deck.tsx index 489bef631..2519d0add 100644 --- a/packages/spectacle/src/components/deck/default-deck.tsx +++ b/packages/spectacle/src/components/deck/default-deck.tsx @@ -5,7 +5,7 @@ import useMousetrap from '../../hooks/use-mousetrap'; import { KEYBOARD_SHORTCUTS, SPECTACLE_MODES, - ToggleModeArgs + ToggleModeParams } from '../../utils/constants'; /** @@ -104,7 +104,7 @@ const DefaultDeck = (props: DefaultDeckProps): JSX.Element => { export default DefaultDeck; type DefaultDeckProps = DeckProps & { - toggleMode(args: ToggleModeArgs): void; + toggleMode(args: ToggleModeParams): void; overviewMode?: boolean; printMode?: boolean; exportMode?: boolean; diff --git a/packages/spectacle/src/hooks/use-modes.ts b/packages/spectacle/src/hooks/use-modes.ts index 3cfa9f9c5..ca6a2e314 100644 --- a/packages/spectacle/src/hooks/use-modes.ts +++ b/packages/spectacle/src/hooks/use-modes.ts @@ -3,7 +3,7 @@ import { parse as parseQS, stringify as stringifyQS } from 'query-string'; import { SPECTACLE_MODES, SpectacleMode, - ToggleModeArgs, + ToggleModeParams, ModeSearchParams } from '../utils/constants'; @@ -17,7 +17,7 @@ const useModes = () => { ); const toggleMode = useCallback( - (args: ToggleModeArgs) => { + (args: ToggleModeParams) => { const { newMode, senderSlideIndex, e } = args; e?.preventDefault(); diff --git a/packages/spectacle/src/utils/constants.ts b/packages/spectacle/src/utils/constants.ts index 033048dde..7bfa2f9e9 100644 --- a/packages/spectacle/src/utils/constants.ts +++ b/packages/spectacle/src/utils/constants.ts @@ -31,7 +31,7 @@ export type ModeSearchParams = { exportMode?: boolean; }; -export type ToggleModeArgs = { +export type ToggleModeParams = { newMode: SpectacleMode; senderSlideIndex?: number; e?: Event; From f4813f1ea4e259460cf86b69f7b24e758bf42e92 Mon Sep 17 00:00:00 2001 From: mwritter Date: Tue, 26 Jul 2022 09:00:37 -0400 Subject: [PATCH 07/21] More Kbar clean up - use internal_shortcut to show we're not using kbar shortcut integration --- .../components/command-bar/command-bar-actions.tsx | 13 +++++++++---- .../src/components/command-bar/results/index.tsx | 11 ++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx index 895ee5d4f..4d2f3d143 100644 --- a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx +++ b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx @@ -1,5 +1,6 @@ import { KEYBOARD_SHORTCUTS, SPECTACLE_MODES } from '../../utils/constants'; import useModes from '../../hooks/use-modes'; +import { ActionImpl } from 'kbar'; const useCommandBarActions = () => { const { toggleMode } = useModes(); @@ -9,7 +10,7 @@ const useCommandBarActions = () => { id: SPECTACLE_MODES.PRESENTER_MODE, name: 'Presenter Mode', keywords: 'presenter', - shortcut: KEYBOARD_SHORTCUTS.PRESENTER_MODE.split('+'), + internal_shortcut: KEYBOARD_SHORTCUTS.PRESENTER_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRESENTER_MODE }), section: 'Mode' }, @@ -17,7 +18,7 @@ const useCommandBarActions = () => { id: SPECTACLE_MODES.OVERVIEW_MODE, name: 'Overview Mode', keywords: 'overview', - shortcut: KEYBOARD_SHORTCUTS.OVERVIEW_MODE.split('+'), + internal_shortcut: KEYBOARD_SHORTCUTS.OVERVIEW_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.OVERVIEW_MODE }), section: 'Mode' }, @@ -25,7 +26,7 @@ const useCommandBarActions = () => { id: SPECTACLE_MODES.PRINT_MODE, name: 'Print Mode', keywords: 'export', - shortcut: KEYBOARD_SHORTCUTS.PRINT_MODE.split('+'), + internal_shortcut: KEYBOARD_SHORTCUTS.PRINT_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRINT_MODE }), section: 'Mode' }, @@ -33,11 +34,15 @@ const useCommandBarActions = () => { id: SPECTACLE_MODES.EXPORT_MODE, name: 'Export Mode', keywords: 'export', - shortcut: KEYBOARD_SHORTCUTS.EXPORT_MODE.split('+'), + internal_shortcut: KEYBOARD_SHORTCUTS.EXPORT_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.EXPORT_MODE }), section: 'Mode' } ]; }; +export type InternalCommand = ActionImpl & { + internal_shortcut?: string[]; +}; + export default useCommandBarActions; diff --git a/packages/spectacle/src/components/command-bar/results/index.tsx b/packages/spectacle/src/components/command-bar/results/index.tsx index 9ac5ed0b6..b5d758b6c 100644 --- a/packages/spectacle/src/components/command-bar/results/index.tsx +++ b/packages/spectacle/src/components/command-bar/results/index.tsx @@ -1,5 +1,7 @@ import { ActionImpl, KBarResults, useMatches } from 'kbar'; +import { useCallback } from 'react'; import styled from 'styled-components'; +import { InternalCommand } from '../command-bar-actions'; interface ResultProps { active: boolean; @@ -32,6 +34,13 @@ const CommandBarResults = (): JSX.Element => { margin-right: 5px; `; + const getShortcutKeys = useCallback((item: InternalCommand) => { + if (item.internal_shortcut) { + return item.internal_shortcut; + } + return item.shortcut ?? []; + }, []); + // TODO: Use platform icons for 'mod', 'shift', and 'alt' keys return ( { {item.name} - {item.shortcut?.map((sc) => ( + {getShortcutKeys(item)?.map((sc) => ( {sc} From 5b2bbbf5acae7d72a525351cb8513f6a5645bbb6 Mon Sep 17 00:00:00 2001 From: mwritter Date: Tue, 26 Jul 2022 16:44:59 -0400 Subject: [PATCH 08/21] Kbar clean up, tried to optimize kbar results --- .../command-bar/command-bar-actions.tsx | 19 +-- .../components/command-bar/results/index.tsx | 121 ++++++++++-------- .../spectacle/src/components/deck/deck.tsx | 5 +- packages/spectacle/src/utils/constants.ts | 17 ++- packages/spectacle/src/utils/platform-keys.ts | 45 +++++++ 5 files changed, 138 insertions(+), 69 deletions(-) create mode 100644 packages/spectacle/src/utils/platform-keys.ts diff --git a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx index 4d2f3d143..d52b1c7bf 100644 --- a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx +++ b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx @@ -1,48 +1,39 @@ -import { KEYBOARD_SHORTCUTS, SPECTACLE_MODES } from '../../utils/constants'; +import { KEYBOARD_SHORTCUTS_IDS, SPECTACLE_MODES } from '../../utils/constants'; import useModes from '../../hooks/use-modes'; -import { ActionImpl } from 'kbar'; const useCommandBarActions = () => { const { toggleMode } = useModes(); return [ { - id: SPECTACLE_MODES.PRESENTER_MODE, + id: KEYBOARD_SHORTCUTS_IDS.PRESENTER_MODE, name: 'Presenter Mode', keywords: 'presenter', - internal_shortcut: KEYBOARD_SHORTCUTS.PRESENTER_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRESENTER_MODE }), section: 'Mode' }, { - id: SPECTACLE_MODES.OVERVIEW_MODE, + id: KEYBOARD_SHORTCUTS_IDS.OVERVIEW_MODE, name: 'Overview Mode', keywords: 'overview', - internal_shortcut: KEYBOARD_SHORTCUTS.OVERVIEW_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.OVERVIEW_MODE }), section: 'Mode' }, { - id: SPECTACLE_MODES.PRINT_MODE, + id: KEYBOARD_SHORTCUTS_IDS.PRINT_MODE, name: 'Print Mode', keywords: 'export', - internal_shortcut: KEYBOARD_SHORTCUTS.PRINT_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRINT_MODE }), section: 'Mode' }, { - id: SPECTACLE_MODES.EXPORT_MODE, + id: KEYBOARD_SHORTCUTS_IDS.EXPORT_MODE, name: 'Export Mode', keywords: 'export', - internal_shortcut: KEYBOARD_SHORTCUTS.EXPORT_MODE.split('+'), perform: () => toggleMode({ newMode: SPECTACLE_MODES.EXPORT_MODE }), section: 'Mode' } ]; }; -export type InternalCommand = ActionImpl & { - internal_shortcut?: string[]; -}; - export default useCommandBarActions; diff --git a/packages/spectacle/src/components/command-bar/results/index.tsx b/packages/spectacle/src/components/command-bar/results/index.tsx index b5d758b6c..3a806f2e9 100644 --- a/packages/spectacle/src/components/command-bar/results/index.tsx +++ b/packages/spectacle/src/components/command-bar/results/index.tsx @@ -1,68 +1,85 @@ -import { ActionImpl, KBarResults, useMatches } from 'kbar'; -import { useCallback } from 'react'; import styled from 'styled-components'; -import { InternalCommand } from '../command-bar-actions'; +import { ActionImpl, KBarResults, useMatches } from 'kbar'; +import { prettifyShortcut } from '../../../utils/platform-keys'; +import { + KeyboardShortcutTypes, + KEYBOARD_SHORTCUTS +} from '../../../utils/constants'; +import { Text } from '../../typography'; -interface ResultProps { +type RenderParams = { + item: ActionImpl | string; active: boolean; -} +}; -const CommandBarResults = (): JSX.Element => { - const { results } = useMatches(); +function getShortcutKeys({ id, shortcut = [] }: ActionImpl): string[] { + if (id in KEYBOARD_SHORTCUTS && !shortcut?.length) { + const _id = id as KeyboardShortcutTypes; + return prettifyShortcut(KEYBOARD_SHORTCUTS[_id].split('+')); + } + return prettifyShortcut(shortcut); +} - const ResultCommand = styled.div` +const ResultCommand = styled.div>` display: flex; justify-content: space-between; - background-color: ${(p) => (p.active ? 'lightgrey' : 'white')}; - color: rgb(28 28 29); - padding: 1rem; + align-items: center + background-color: ${(p) => (p.active ? 'lightsteelblue' : 'transparent')}; + padding: 0.5rem 1rem; + cursor: pointer; `; - const ResultSectionHeader = styled.div` - background-color: white; - padding-left: 1rem; - border-bottom: 1px solid rgba(0 0 0 / 0.1); - font-size: smaller; - margin: 0 1rem; - `; +const ResultSectionHeader = styled.div` + background-color: white; + padding-left: 1rem; + border-bottom: 1px solid rgba(0 0 0 / 0.1); + font-size: smaller; + margin: 0 1rem; +`; - const ResultShortcutKey = styled.span` - padding: 5px 10px; - background: rgba(0 0 0 / 0.1); - border-radius: 4px; - fontsize: 14; - margin-right: 5px; - `; +const ResultShortcut = styled.span` + display: flex; + gap: 5px; + font-size: 1.3em; +`; - const getShortcutKeys = useCallback((item: InternalCommand) => { - if (item.internal_shortcut) { - return item.internal_shortcut; - } - return item.shortcut ?? []; - }, []); +const ResultShortcutKey = styled.kbd` + background-color: #eee; + border-radius: 3px; + border: 1px solid #b4b4b4; + padding: 5px 10px; + white-space: nowrap; +`; - // TODO: Use platform icons for 'mod', 'shift', and 'alt' keys - return ( - - typeof item === 'string' ? ( - {item} - ) : ( - - {item.name} - - {getShortcutKeys(item)?.map((sc) => ( - - {sc} +function onRender({ item, active }: RenderParams): JSX.Element { + if (typeof item === 'string') { + return ( + + {item} + + ); + } else { + return ( + + {item.name} + + {getShortcutKeys(item).map( + (key) => + key && ( + + {key} - ))} - - - ) - } - /> - ); + ) + )} + + + ); + } +} + +const CommandBarResults = (): JSX.Element => { + const { results } = useMatches(); + return ; }; export default CommandBarResults; diff --git a/packages/spectacle/src/components/deck/deck.tsx b/packages/spectacle/src/components/deck/deck.tsx index 85274e89e..20e2282ff 100644 --- a/packages/spectacle/src/components/deck/deck.tsx +++ b/packages/spectacle/src/components/deck/deck.tsx @@ -39,6 +39,7 @@ import { SwipeEventData } from 'react-swipeable'; import { MarkdownComponentMap } from '../../utils/mdx-component-mapper'; import TemplateWrapper from '../template-wrapper'; import { useRegisterActions } from 'kbar'; +import { KEYBOARD_SHORTCUTS_IDS } from '../../utils/constants'; export type DeckContextType = { deckId: string | number; @@ -223,14 +224,14 @@ export const DeckInternal = forwardRef( !disableInteractivity ? [ { - id: 'Next Slide', + id: KEYBOARD_SHORTCUTS_IDS.NEXT_SLIDE, name: 'Next Slide', keywords: 'next', perform: () => stepForward(), section: 'Slides' }, { - id: 'Previous Slide', + id: KEYBOARD_SHORTCUTS_IDS.PREVIOUS_SLIDE, name: 'Previous Slide', keywords: 'previous', perform: () => stepBackward(), diff --git a/packages/spectacle/src/utils/constants.ts b/packages/spectacle/src/utils/constants.ts index 7bfa2f9e9..8dc52768c 100644 --- a/packages/spectacle/src/utils/constants.ts +++ b/packages/spectacle/src/utils/constants.ts @@ -11,7 +11,22 @@ export const KEYBOARD_SHORTCUTS = { EXPORT_MODE: 'mod+shift+e', TAB_FORWARD_OVERVIEW_MODE: 'tab', TAB_BACKWARD_OVERVIEW_MODE: 'shift+tab', - SELECT_SLIDE_OVERVIEW_MODE: 'enter' + SELECT_SLIDE_OVERVIEW_MODE: 'enter', + NEXT_SLIDE: 'right', + PREVIOUS_SLIDE: 'left' +}; +export type KeyboardShortcutTypes = keyof typeof KEYBOARD_SHORTCUTS; + +export const KEYBOARD_SHORTCUTS_IDS = { + PRESENTER_MODE: 'PRESENTER_MODE', + OVERVIEW_MODE: 'OVERVIEW_MODE', + PRINT_MODE: 'PRINT_MODE', + EXPORT_MODE: 'EXPORT_MODE', + TAB_FORWARD_OVERVIEW_MODE: 'TAB_FORWARD_OVERVIEW_MODE', + TAB_BACKWARD_OVERVIEW_MODE: 'TAB_BACKWARD_OVERVIEW_MODE', + SELECT_SLIDE_OVERVIEW_MODE: 'SELECT_SLIDE_OVERVIEW_MODE', + NEXT_SLIDE: 'NEXT_SLIDE', + PREVIOUS_SLIDE: 'PREVIOUS_SLIDE' }; export const SPECTACLE_MODES = { diff --git a/packages/spectacle/src/utils/platform-keys.ts b/packages/spectacle/src/utils/platform-keys.ts new file mode 100644 index 000000000..ae5a41726 --- /dev/null +++ b/packages/spectacle/src/utils/platform-keys.ts @@ -0,0 +1,45 @@ +/* + * Check if operating system is MacOS + */ +export function isPlatformMacOS() { + return /Mac|iPad|iPhone/.test(navigator.userAgent); +} + +/* + * Get operating system specific key + */ +export function getKeyForOS(key: KeyType) { + const isMacOS = isPlatformMacOS(); + + const replacementKeyMap = { + alt: isMacOS ? '⌥' : 'Alt', + ctrl: isMacOS ? '^' : 'Ctrl', + mod: isMacOS ? '⌘' : 'Ctrl', + shift: isMacOS ? '⇧' : 'Shift' + }; + + return replacementKeyMap[key] ?? key; +} + +/** + * Prettifies keyboard shortcuts in a platform-agnostic way. + */ +export function prettifyShortcut(shortcut: string[]): string[] { + return shortcut + .join('+') + .toLowerCase() + .replace('alt', getKeyForOS('alt')) + .replace('ctrl', getKeyForOS('ctrl')) + .replace('mod', getKeyForOS('mod')) + .replace('shift', getKeyForOS('shift')) + .replace('left', '←') + .replace('up', '↑') + .replace('right', '→') + .replace('down', '↓') + .replace('delete', '⌫') + .replace('enter', '⏎') + .split('+') + .map((s) => s.charAt(0).toUpperCase() + s.slice(1)); +} + +export type KeyType = 'alt' | 'ctrl' | 'mod' | 'shift'; From dbc04debca852772310e2a89c87ac79475492c3d Mon Sep 17 00:00:00 2001 From: mwritter Date: Tue, 26 Jul 2022 16:51:12 -0400 Subject: [PATCH 09/21] clean up --- packages/spectacle/src/components/command-bar/index.tsx | 3 +-- .../spectacle/src/components/command-bar/search/index.tsx | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/spectacle/src/components/command-bar/index.tsx b/packages/spectacle/src/components/command-bar/index.tsx index 633cabd59..a4fe83f28 100644 --- a/packages/spectacle/src/components/command-bar/index.tsx +++ b/packages/spectacle/src/components/command-bar/index.tsx @@ -3,8 +3,7 @@ import { KBarProvider } from 'kbar'; import useCommandBarActions from './command-bar-actions'; import CommandBarSearch from './search'; -const CommandBar = (props: CommandBarProps): JSX.Element => { - const { children } = props; +const CommandBar = ({ children }: CommandBarProps): JSX.Element => { const actions = useCommandBarActions(); return ( diff --git a/packages/spectacle/src/components/command-bar/search/index.tsx b/packages/spectacle/src/components/command-bar/search/index.tsx index 5794300bf..19151b908 100644 --- a/packages/spectacle/src/components/command-bar/search/index.tsx +++ b/packages/spectacle/src/components/command-bar/search/index.tsx @@ -1,6 +1,6 @@ -import CommandBarResults from '../results'; -import { KBarPortal, KBarPositioner, KBarAnimator, KBarSearch } from 'kbar'; import styled from 'styled-components'; +import { KBarPortal, KBarPositioner, KBarAnimator, KBarSearch } from 'kbar'; +import CommandBarResults from '../results'; const KBarSearchStyled = styled(KBarSearch)` padding: 12px 16px; From 5676874c3f03eae03d1c8d2d3fd0ad70e95f0959 Mon Sep 17 00:00:00 2001 From: mwritter Date: Wed, 27 Jul 2022 08:53:31 -0400 Subject: [PATCH 10/21] little refactor --- .../spectacle/src/components/deck/index.tsx | 67 +++++++++++-------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/packages/spectacle/src/components/deck/index.tsx b/packages/spectacle/src/components/deck/index.tsx index 700be5d70..d2675c6fc 100644 --- a/packages/spectacle/src/components/deck/index.tsx +++ b/packages/spectacle/src/components/deck/index.tsx @@ -3,11 +3,47 @@ import DefaultDeck from './default-deck'; import PresenterMode from '../presenter-mode'; import PrintMode from '../print-mode'; import useMousetrap from '../../hooks/use-mousetrap'; -import { KEYBOARD_SHORTCUTS, SPECTACLE_MODES } from '../../utils/constants'; +import { + KEYBOARD_SHORTCUTS, + SPECTACLE_MODES, + ToggleModeParams +} from '../../utils/constants'; import { DeckProps } from './deck'; import useModes from '../../hooks/use-modes'; import CommandBar from '../command-bar'; +type ViewProps = DeckProps & { + mode: string; + toggleMode: (args: ToggleModeParams) => void; +}; + +const View = ({ mode, toggleMode, ...props }: ViewProps): JSX.Element => { + switch (mode) { + case SPECTACLE_MODES.DEFAULT_MODE: + return ; + + case SPECTACLE_MODES.PRESENTER_MODE: + return ; + + /** + * Print mode and export mode are identical except for the theme + * that is used. Print mode uses the print theme which is usually + * monotone and export mode uses the default theme. + */ + case SPECTACLE_MODES.PRINT_MODE: + return ; + + case SPECTACLE_MODES.EXPORT_MODE: + return ; + + case SPECTACLE_MODES.OVERVIEW_MODE: + return ; + + default: + return ; + } +}; + const SpectacleDeck = (props: DeckProps): JSX.Element => { const { toggleMode, currentMode } = useModes(); @@ -25,36 +61,9 @@ const SpectacleDeck = (props: DeckProps): JSX.Element => { [] ); - const View = () => { - switch (currentMode) { - case SPECTACLE_MODES.DEFAULT_MODE: - return ; - - case SPECTACLE_MODES.PRESENTER_MODE: - return ; - - /** - * Print mode and export mode are identical except for the theme - * that is used. Print mode uses the print theme which is usually - * monotone and export mode uses the default theme. - */ - case SPECTACLE_MODES.PRINT_MODE: - return ; - - case SPECTACLE_MODES.EXPORT_MODE: - return ; - - case SPECTACLE_MODES.OVERVIEW_MODE: - return ; - - default: - return ; - } - }; - return ( - + ); }; From 11f7dfdeae7887e0af4334913f50847cfcaec094 Mon Sep 17 00:00:00 2001 From: mwritter Date: Wed, 27 Jul 2022 10:59:35 -0400 Subject: [PATCH 11/21] Added test for platform-key util --- .../components/command-bar/results/index.tsx | 2 +- .../spectacle/src/utils/platform-keys.test.ts | 81 +++++++++++++++++++ packages/spectacle/src/utils/platform-keys.ts | 15 ++-- 3 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 packages/spectacle/src/utils/platform-keys.test.ts diff --git a/packages/spectacle/src/components/command-bar/results/index.tsx b/packages/spectacle/src/components/command-bar/results/index.tsx index 3a806f2e9..66fc99912 100644 --- a/packages/spectacle/src/components/command-bar/results/index.tsx +++ b/packages/spectacle/src/components/command-bar/results/index.tsx @@ -15,7 +15,7 @@ type RenderParams = { function getShortcutKeys({ id, shortcut = [] }: ActionImpl): string[] { if (id in KEYBOARD_SHORTCUTS && !shortcut?.length) { const _id = id as KeyboardShortcutTypes; - return prettifyShortcut(KEYBOARD_SHORTCUTS[_id].split('+')); + return prettifyShortcut(KEYBOARD_SHORTCUTS[_id]); } return prettifyShortcut(shortcut); } diff --git a/packages/spectacle/src/utils/platform-keys.test.ts b/packages/spectacle/src/utils/platform-keys.test.ts new file mode 100644 index 000000000..4562c9375 --- /dev/null +++ b/packages/spectacle/src/utils/platform-keys.test.ts @@ -0,0 +1,81 @@ +import { + isPlatformMacOS, + getKeyForOS, + prettifyShortcut +} from './platform-keys'; + +describe('isPlatformMacOS', () => { + it('should return true for MacIntel', () => { + Object.defineProperty(navigator, 'userAgent', { + value: 'Mac', + configurable: true + }); + expect(isPlatformMacOS()).toBe(true); + }); + + it('Return false for anything that doesnt contain Mac|iPad', () => { + Object.defineProperty(navigator, 'userAgent', { + value: 'Windows', + configurable: true + }); + expect(isPlatformMacOS()).toBe(false); + }); + + Object.defineProperty(navigator, 'userAgent', { + value: 'Linux', + configurable: true + }); + expect(isPlatformMacOS()).toBe(false); +}); + +describe('getKeyForOS', () => { + it.each` + userAgent | key | result + ${'MacIntel'} | ${'mod'} | ${'⌘'} + ${'Windows'} | ${'mod'} | ${'Ctrl'} + ${'MacIntel'} | ${'shift'} | ${'⇧'} + ${'Windows'} | ${'shift'} | ${'Shift'} + ${'MacIntel'} | ${'ctrl'} | ${'^'} + ${'Windows'} | ${'ctrl'} | ${'Ctrl'} + ${'MacIntel'} | ${'alt'} | ${'⌥'} + ${'Windows'} | ${'alt'} | ${'Alt'} + `( + 'should return $result for $key on $userAgent', + ({ userAgent, key, result }) => { + Object.defineProperty(navigator, 'userAgent', { + value: userAgent, + configurable: true + }); + + expect(getKeyForOS(key)).toStrictEqual(result); + } + ); +}); + +describe('prettifyShortcut', () => { + it.each` + userAgent | shortcut | result + ${'MacIntel'} | ${'mod+shift+p'} | ${['⌘', '⇧', 'P']} + ${'Windows'} | ${'mod+shift+p'} | ${['Ctrl', 'Shift', 'P']} + ${'MacIntel'} | ${'mod+shift+o'} | ${['⌘', '⇧', 'O']} + ${'Windows'} | ${'mod+shift+o'} | ${['Ctrl', 'Shift', 'O']} + ${'MacIntel'} | ${['mod', 'shift', 'r']} | ${['⌘', '⇧', 'R']} + ${'Windows'} | ${['mod', 'shift', 'r']} | ${['Ctrl', 'Shift', 'R']} + ${'MacIntel'} | ${['mod', 'shift', 'e']} | ${['⌘', '⇧', 'E']} + ${'Windows'} | ${['mod', 'shift', 'e']} | ${['Ctrl', 'Shift', 'E']} + ${'MacIntel'} | ${'left'} | ${['←']} + ${'Windows'} | ${'left'} | ${['←']} + ${'MacIntel'} | ${'right'} | ${['→']} + ${'Windows'} | ${'right'} | ${['→']} + `( + 'should return $result for $shortcut on $userAgent', + ({ userAgent, shortcut, result }) => { + Object.defineProperty(navigator, 'userAgent', { + value: userAgent, + configurable: true + }); + + expect(prettifyShortcut(shortcut)).toStrictEqual(result); + } + ); +}); diff --git a/packages/spectacle/src/utils/platform-keys.ts b/packages/spectacle/src/utils/platform-keys.ts index ae5a41726..e5a5e87c4 100644 --- a/packages/spectacle/src/utils/platform-keys.ts +++ b/packages/spectacle/src/utils/platform-keys.ts @@ -2,7 +2,7 @@ * Check if operating system is MacOS */ export function isPlatformMacOS() { - return /Mac|iPad|iPhone/.test(navigator.userAgent); + return /Mac|iPad/.test(navigator.userAgent); } /* @@ -18,26 +18,23 @@ export function getKeyForOS(key: KeyType) { shift: isMacOS ? '⇧' : 'Shift' }; - return replacementKeyMap[key] ?? key; + return replacementKeyMap[key]; } /** * Prettifies keyboard shortcuts in a platform-agnostic way. */ -export function prettifyShortcut(shortcut: string[]): string[] { - return shortcut - .join('+') +export function prettifyShortcut(shortcut: string[] | string): string[] { + const _shortcut = + typeof shortcut === 'string' ? shortcut : shortcut.join('+'); + return _shortcut .toLowerCase() .replace('alt', getKeyForOS('alt')) .replace('ctrl', getKeyForOS('ctrl')) .replace('mod', getKeyForOS('mod')) .replace('shift', getKeyForOS('shift')) .replace('left', '←') - .replace('up', '↑') .replace('right', '→') - .replace('down', '↓') - .replace('delete', '⌫') - .replace('enter', '⏎') .split('+') .map((s) => s.charAt(0).toUpperCase() + s.slice(1)); } From c47512bbb493ec06f4aeeb556e3a13cb99f45ed1 Mon Sep 17 00:00:00 2001 From: mwritter Date: Wed, 27 Jul 2022 16:17:07 -0400 Subject: [PATCH 12/21] Added test for use-modes --- .../spectacle/src/components/deck/index.tsx | 28 +++++------ .../spectacle/src/hooks/use-modes.test.ts | 46 +++++++++++++++++++ packages/spectacle/src/hooks/use-modes.ts | 11 ++++- 3 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 packages/spectacle/src/hooks/use-modes.test.ts diff --git a/packages/spectacle/src/components/deck/index.tsx b/packages/spectacle/src/components/deck/index.tsx index d2675c6fc..7e8988bc0 100644 --- a/packages/spectacle/src/components/deck/index.tsx +++ b/packages/spectacle/src/components/deck/index.tsx @@ -3,21 +3,17 @@ import DefaultDeck from './default-deck'; import PresenterMode from '../presenter-mode'; import PrintMode from '../print-mode'; import useMousetrap from '../../hooks/use-mousetrap'; -import { - KEYBOARD_SHORTCUTS, - SPECTACLE_MODES, - ToggleModeParams -} from '../../utils/constants'; +import { KEYBOARD_SHORTCUTS, SPECTACLE_MODES } from '../../utils/constants'; import { DeckProps } from './deck'; -import useModes from '../../hooks/use-modes'; +import useModes, { ModeActions } from '../../hooks/use-modes'; import CommandBar from '../command-bar'; -type ViewProps = DeckProps & { - mode: string; - toggleMode: (args: ToggleModeParams) => void; -}; - -const View = ({ mode, toggleMode, ...props }: ViewProps): JSX.Element => { +const View = ({ + getCurrentMode, + toggleMode, + ...props +}: ModeActions & DeckProps): JSX.Element => { + const mode = getCurrentMode(); switch (mode) { case SPECTACLE_MODES.DEFAULT_MODE: return ; @@ -45,7 +41,7 @@ const View = ({ mode, toggleMode, ...props }: ViewProps): JSX.Element => { }; const SpectacleDeck = (props: DeckProps): JSX.Element => { - const { toggleMode, currentMode } = useModes(); + const { toggleMode, getCurrentMode } = useModes(); useMousetrap( { @@ -63,7 +59,11 @@ const SpectacleDeck = (props: DeckProps): JSX.Element => { return ( - + ); }; diff --git a/packages/spectacle/src/hooks/use-modes.test.ts b/packages/spectacle/src/hooks/use-modes.test.ts new file mode 100644 index 000000000..32f762bef --- /dev/null +++ b/packages/spectacle/src/hooks/use-modes.test.ts @@ -0,0 +1,46 @@ +import '@testing-library/jest-dom'; +import { renderHook } from '@testing-library/react'; +import { SPECTACLE_MODES } from '../utils/constants'; +import useModes from './use-modes'; + +describe('useModes', () => { + Object.defineProperty(window, 'location', { + value: { + search: 'slideIndex=0&stepIndex=0' + } + }); + describe('toggleMode and currentMode', () => { + it('should set the window.location based on the spectacle modes', () => { + const { result } = renderHook(() => useModes()); + + // Default + expect(result.current.getCurrentMode()).toBe( + SPECTACLE_MODES.DEFAULT_MODE + ); + + // Presenter + result.current.toggleMode({ newMode: SPECTACLE_MODES.PRESENTER_MODE }); + expect(location.search).toMatch(/^presenterMode=true/); + expect(result.current.getCurrentMode()).toBe( + SPECTACLE_MODES.PRESENTER_MODE + ); + + // Overview + result.current.toggleMode({ newMode: SPECTACLE_MODES.OVERVIEW_MODE }); + expect(location.search).toMatch(/^overviewMode/); + expect(result.current.getCurrentMode()).toBe( + SPECTACLE_MODES.OVERVIEW_MODE + ); + + // Print + result.current.toggleMode({ newMode: SPECTACLE_MODES.PRINT_MODE }); + expect(location.search).toMatch(/^printMode=true/); + expect(result.current.getCurrentMode()).toBe(SPECTACLE_MODES.PRINT_MODE); + + // Export + result.current.toggleMode({ newMode: SPECTACLE_MODES.EXPORT_MODE }); + expect(location.search).toMatch(/^exportMode=true/); + expect(result.current.getCurrentMode()).toBe(SPECTACLE_MODES.EXPORT_MODE); + }); + }); +}); diff --git a/packages/spectacle/src/hooks/use-modes.ts b/packages/spectacle/src/hooks/use-modes.ts index ca6a2e314..a992e1fe6 100644 --- a/packages/spectacle/src/hooks/use-modes.ts +++ b/packages/spectacle/src/hooks/use-modes.ts @@ -7,7 +7,7 @@ import { ModeSearchParams } from '../utils/constants'; -const useModes = () => { +const useModes = (): ModeActions => { const mode = useRef( modeKeyForSearchParam( parseQS(location.search, { @@ -51,7 +51,9 @@ const useModes = () => { [mode] ); - return { toggleMode, currentMode: mode.current }; + const getCurrentMode = useCallback((): SpectacleMode => mode.current, []); + + return { toggleMode, getCurrentMode }; }; function modeSearchParamForKey(key: SpectacleMode): ModeSearchParams { @@ -85,4 +87,9 @@ function modeKeyForSearchParam({ return SPECTACLE_MODES.DEFAULT_MODE; } +export type ModeActions = { + toggleMode: (args: ToggleModeParams) => void; + getCurrentMode: () => SpectacleMode; +}; + export default useModes; From 394b428e9c895a92bf96f67f059bd68d27deaf8a Mon Sep 17 00:00:00 2001 From: mwritter Date: Wed, 27 Jul 2022 16:32:13 -0400 Subject: [PATCH 13/21] pnpm --- pnpm-lock.yaml | 64 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec79d9bad..6d91a6900 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -213,6 +213,7 @@ importers: history: ^4.9.0 html-webpack-plugin: ^5.5.0 jest: ^27.3.1 + kbar: 0.1.0-beta.36 mdast-builder: ^1.1.1 mdast-zone: ^4.0.0 merge-anything: ^3.0.3 @@ -250,6 +251,7 @@ importers: broadcastchannel-polyfill: 1.0.1 dedent: 0.7.0 history: 4.10.1 + kbar: 0.1.0-beta.36_biqbaboplfbrettd7655fr4n2y mdast-builder: 1.1.1 mdast-zone: 4.0.1 merge-anything: 3.0.7 @@ -2169,6 +2171,35 @@ packages: fastq: 1.13.0 dev: true + /@reach/observe-rect/1.2.0: + resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==} + dev: false + + /@reach/portal/0.16.2_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-9ur/yxNkuVYTIjAcfi46LdKUvH0uYZPfEp4usWcpt6PIp+WDF57F/5deMe/uGi/B/nfDweQu8VVwuMVrCb97JQ==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + '@reach/utils': 0.16.0_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + tiny-warning: 1.0.3 + tslib: 2.4.0 + dev: false + + /@reach/utils/0.16.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-PCggBet3qaQmwFNcmQ/GqHSefadAFyNCUekq9RrWoaU9hh/S4iaFgf2MBMdM47eQj5i/Bk0Mm07cP/XPFlkN+Q==} + peerDependencies: + react: ^16.8.0 || 17.x + react-dom: ^16.8.0 || 17.x + dependencies: + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + tiny-warning: 1.0.3 + tslib: 2.4.0 + dev: false + /@sinonjs/commons/1.8.3: resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==} dependencies: @@ -3729,6 +3760,10 @@ packages: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} dev: false + /command-score/0.1.2: + resolution: {integrity: sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==} + dev: false + /commander/2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true @@ -4646,6 +4681,10 @@ packages: resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} dev: true + /fast-equals/2.0.4: + resolution: {integrity: sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==} + dev: false + /fast-glob/3.2.11: resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} engines: {node: '>=8.6.0'} @@ -6251,6 +6290,21 @@ packages: object.assign: 4.1.2 dev: true + /kbar/0.1.0-beta.36_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-i5tU7VYkMmxHCoyG5qzkNeU3qViKBz2F0fjqvWWSKsgVABCF3BjxzAH570Mhn3Zy92x3NGZae8emkBpEk7MKgw==} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@reach/portal': 0.16.2_biqbaboplfbrettd7655fr4n2y + command-score: 0.1.2 + fast-equals: 2.0.4 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-virtual: 2.10.4_react@18.2.0 + tiny-invariant: 1.2.0 + dev: false + /kind-of/3.2.2: resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} engines: {node: '>=0.10.0'} @@ -7326,6 +7380,15 @@ packages: refractor: 3.6.0 dev: false + /react-virtual/2.10.4_react@18.2.0: + resolution: {integrity: sha512-Ir6+oPQZTVHfa6+JL9M7cvMILstFZH/H3jqeYeKI4MSUX+rIruVwFC6nGVXw9wqAw8L0Kg2KvfXxI85OvYQdpQ==} + peerDependencies: + react: ^16.6.3 || ^17.0.0 + dependencies: + '@reach/observe-rect': 1.2.0 + react: 18.2.0 + dev: false + /react/18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -8457,7 +8520,6 @@ packages: /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - dev: true /tsutils/3.21.0_typescript@4.7.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} From 80062f60c31bf6f9b35acaa11580e8a72050eaf8 Mon Sep 17 00:00:00 2001 From: mwritter Date: Thu, 28 Jul 2022 09:27:42 -0400 Subject: [PATCH 14/21] Fixing tests --- .../src/components/markdown/markdown.test.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/spectacle/src/components/markdown/markdown.test.tsx b/packages/spectacle/src/components/markdown/markdown.test.tsx index 0b7f37d50..1bcea9c10 100644 --- a/packages/spectacle/src/components/markdown/markdown.test.tsx +++ b/packages/spectacle/src/components/markdown/markdown.test.tsx @@ -1,10 +1,18 @@ import { ReactElement } from 'react'; import { Markdown, MarkdownSlide, MarkdownSlideSet } from './markdown'; -import Deck from '../deck/deck'; -import { Heading, ListItem } from '../typography'; -import { Appear } from '../appear'; +import Deck from '../deck'; +import { Heading } from '../typography'; import Slide from '../slide/slide'; -import { queryByTestId, render } from '@testing-library/react'; +import { render } from '@testing-library/react'; + +jest.mock('../../hooks/use-broadcast-channel', () => { + return { + __esModule: true, + default: function useModes() { + return [() => {}]; + } + }; +}); const mountInsideDeck = (tree: ReactElement) => { return render({tree}); From abf954c63c4919ddcc42154980a12ef88b957a4b Mon Sep 17 00:00:00 2001 From: mwritter Date: Thu, 28 Jul 2022 09:32:03 -0400 Subject: [PATCH 15/21] Fixing tests --- .../spectacle/src/components/slide-layout.test.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/spectacle/src/components/slide-layout.test.tsx b/packages/spectacle/src/components/slide-layout.test.tsx index a981bc9d5..f713cc18a 100644 --- a/packages/spectacle/src/components/slide-layout.test.tsx +++ b/packages/spectacle/src/components/slide-layout.test.tsx @@ -1,9 +1,18 @@ import { ReactElement } from 'react'; import { render } from '@testing-library/react'; -import Deck from './deck/deck'; +import Deck from './deck'; import SlideLayout from './slide-layout'; import { Heading, Text } from './typography'; +jest.mock('../hooks/use-broadcast-channel', () => { + return { + __esModule: true, + default: function useModes() { + return [() => {}]; + } + }; +}); + const renderInDeck = (tree: ReactElement | JSX.Element) => render({tree}); From 4697ae24c0759c38e3a9e811763652ef32974b64 Mon Sep 17 00:00:00 2001 From: mwritter Date: Thu, 28 Jul 2022 09:34:10 -0400 Subject: [PATCH 16/21] oof --- packages/spectacle/src/components/markdown/markdown.test.tsx | 2 +- packages/spectacle/src/components/slide-layout.test.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/spectacle/src/components/markdown/markdown.test.tsx b/packages/spectacle/src/components/markdown/markdown.test.tsx index 1bcea9c10..2b2274c2b 100644 --- a/packages/spectacle/src/components/markdown/markdown.test.tsx +++ b/packages/spectacle/src/components/markdown/markdown.test.tsx @@ -8,7 +8,7 @@ import { render } from '@testing-library/react'; jest.mock('../../hooks/use-broadcast-channel', () => { return { __esModule: true, - default: function useModes() { + default: function useBroadcastChannel() { return [() => {}]; } }; diff --git a/packages/spectacle/src/components/slide-layout.test.tsx b/packages/spectacle/src/components/slide-layout.test.tsx index f713cc18a..b34f971dd 100644 --- a/packages/spectacle/src/components/slide-layout.test.tsx +++ b/packages/spectacle/src/components/slide-layout.test.tsx @@ -7,7 +7,7 @@ import { Heading, Text } from './typography'; jest.mock('../hooks/use-broadcast-channel', () => { return { __esModule: true, - default: function useModes() { + default: function useBroadcastChannel() { return [() => {}]; } }; From b4b24954178193835e390caa385dfcbb883a60f3 Mon Sep 17 00:00:00 2001 From: mwritter Date: Thu, 28 Jul 2022 13:18:06 -0400 Subject: [PATCH 17/21] Add some test clean up --- .../spectacle/src/hooks/use-modes.test.ts | 19 ++- packages/spectacle/src/hooks/use-modes.ts | 8 +- .../spectacle/src/utils/platform-keys.test.ts | 145 ++++++++++-------- 3 files changed, 99 insertions(+), 73 deletions(-) diff --git a/packages/spectacle/src/hooks/use-modes.test.ts b/packages/spectacle/src/hooks/use-modes.test.ts index 32f762bef..291a0399e 100644 --- a/packages/spectacle/src/hooks/use-modes.test.ts +++ b/packages/spectacle/src/hooks/use-modes.test.ts @@ -3,14 +3,23 @@ import { renderHook } from '@testing-library/react'; import { SPECTACLE_MODES } from '../utils/constants'; import useModes from './use-modes'; +const { location: locationBefore } = window; + describe('useModes', () => { - Object.defineProperty(window, 'location', { - value: { - search: 'slideIndex=0&stepIndex=0' - } + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { + search: 'slideIndex=0&stepIndex=0' + }, + writable: true + }); + }); + + afterAll(() => { + window.location = locationBefore; }); describe('toggleMode and currentMode', () => { - it('should set the window.location based on the spectacle modes', () => { + it('should set the window.location and current mode based on the spectacle modes', () => { const { result } = renderHook(() => useModes()); // Default diff --git a/packages/spectacle/src/hooks/use-modes.ts b/packages/spectacle/src/hooks/use-modes.ts index a992e1fe6..0238bce3b 100644 --- a/packages/spectacle/src/hooks/use-modes.ts +++ b/packages/spectacle/src/hooks/use-modes.ts @@ -10,7 +10,7 @@ import { const useModes = (): ModeActions => { const mode = useRef( modeKeyForSearchParam( - parseQS(location.search, { + parseQS(window.location.search, { parseBooleans: true }) ) @@ -23,7 +23,7 @@ const useModes = (): ModeActions => { let stepIndex: string | number = 0; let slideIndex: string | number = senderSlideIndex || ''; - const searchParams = parseQS(location.search, { + const searchParams = parseQS(window.location.search, { parseBooleans: true }); @@ -33,7 +33,7 @@ const useModes = (): ModeActions => { } if (mode.current === newMode) { - location.search = stringifyQS({ + window.location.search = stringifyQS({ slideIndex, stepIndex }); @@ -42,7 +42,7 @@ const useModes = (): ModeActions => { mode.current = newMode; - location.search = stringifyQS({ + window.location.search = stringifyQS({ slideIndex, stepIndex, ...modeSearchParamForKey(newMode) diff --git a/packages/spectacle/src/utils/platform-keys.test.ts b/packages/spectacle/src/utils/platform-keys.test.ts index 4562c9375..4dadffa81 100644 --- a/packages/spectacle/src/utils/platform-keys.test.ts +++ b/packages/spectacle/src/utils/platform-keys.test.ts @@ -4,78 +4,95 @@ import { prettifyShortcut } from './platform-keys'; -describe('isPlatformMacOS', () => { - it('should return true for MacIntel', () => { - Object.defineProperty(navigator, 'userAgent', { - value: 'Mac', - configurable: true - }); - expect(isPlatformMacOS()).toBe(true); - }); +const { navigator: navigatorBefore } = window; - it('Return false for anything that doesnt contain Mac|iPad', () => { - Object.defineProperty(navigator, 'userAgent', { - value: 'Windows', - configurable: true +describe('platform-keys', () => { + beforeAll(() => { + Object.defineProperty(window, 'navigator', { + writable: true }); - expect(isPlatformMacOS()).toBe(false); }); - Object.defineProperty(navigator, 'userAgent', { - value: 'Linux', - configurable: true + afterAll(() => { + window.navigator = navigatorBefore; }); - expect(isPlatformMacOS()).toBe(false); -}); - -describe('getKeyForOS', () => { - it.each` - userAgent | key | result - ${'MacIntel'} | ${'mod'} | ${'⌘'} - ${'Windows'} | ${'mod'} | ${'Ctrl'} - ${'MacIntel'} | ${'shift'} | ${'⇧'} - ${'Windows'} | ${'shift'} | ${'Shift'} - ${'MacIntel'} | ${'ctrl'} | ${'^'} - ${'Windows'} | ${'ctrl'} | ${'Ctrl'} - ${'MacIntel'} | ${'alt'} | ${'⌥'} - ${'Windows'} | ${'alt'} | ${'Alt'} - `( - 'should return $result for $key on $userAgent', - ({ userAgent, key, result }) => { - Object.defineProperty(navigator, 'userAgent', { - value: userAgent, - configurable: true + describe('isPlatformMacOS', () => { + it('should return true for MacIntel', () => { + Object.defineProperty(window, 'navigator', { + value: { + userAgent: 'MacIntel' + } }); - expect(getKeyForOS(key)).toStrictEqual(result); - } - ); -}); + expect(isPlatformMacOS()).toBe(true); + }); -describe('prettifyShortcut', () => { - it.each` - userAgent | shortcut | result - ${'MacIntel'} | ${'mod+shift+p'} | ${['⌘', '⇧', 'P']} - ${'Windows'} | ${'mod+shift+p'} | ${['Ctrl', 'Shift', 'P']} - ${'MacIntel'} | ${'mod+shift+o'} | ${['⌘', '⇧', 'O']} - ${'Windows'} | ${'mod+shift+o'} | ${['Ctrl', 'Shift', 'O']} - ${'MacIntel'} | ${['mod', 'shift', 'r']} | ${['⌘', '⇧', 'R']} - ${'Windows'} | ${['mod', 'shift', 'r']} | ${['Ctrl', 'Shift', 'R']} - ${'MacIntel'} | ${['mod', 'shift', 'e']} | ${['⌘', '⇧', 'E']} - ${'Windows'} | ${['mod', 'shift', 'e']} | ${['Ctrl', 'Shift', 'E']} - ${'MacIntel'} | ${'left'} | ${['←']} - ${'Windows'} | ${'left'} | ${['←']} - ${'MacIntel'} | ${'right'} | ${['→']} - ${'Windows'} | ${'right'} | ${['→']} - `( - 'should return $result for $shortcut on $userAgent', - ({ userAgent, shortcut, result }) => { - Object.defineProperty(navigator, 'userAgent', { - value: userAgent, - configurable: true + it('Return false for anything that doesnt contain Mac|darwin|iPad', () => { + Object.defineProperty(window, 'navigator', { + value: { + userAgent: 'Windows' + } }); + expect(isPlatformMacOS()).toBe(false); - expect(prettifyShortcut(shortcut)).toStrictEqual(result); - } - ); + Object.defineProperty(window, 'navigator', { + value: { + userAgent: 'Linux' + } + }); + expect(isPlatformMacOS()).toBe(false); + }); + }); + + describe('getKeyForOS', () => { + it.each` + userAgent | key | result + ${'MacIntel'} | ${'mod'} | ${'⌘'} + ${'Windows'} | ${'mod'} | ${'Ctrl'} + ${'MacIntel'} | ${'shift'} | ${'⇧'} + ${'Windows'} | ${'shift'} | ${'Shift'} + ${'MacIntel'} | ${'ctrl'} | ${'^'} + ${'Windows'} | ${'ctrl'} | ${'Ctrl'} + ${'MacIntel'} | ${'alt'} | ${'⌥'} + ${'Windows'} | ${'alt'} | ${'Alt'} + `( + 'should return $result for $key on $userAgent', + ({ userAgent, key, result }) => { + Object.defineProperty(navigator, 'userAgent', { + value: userAgent, + configurable: true + }); + + expect(getKeyForOS(key)).toStrictEqual(result); + } + ); + }); + + describe('prettifyShortcut', () => { + it.each` + userAgent | shortcut | result + ${'MacIntel'} | ${'mod+shift+p'} | ${['⌘', '⇧', 'P']} + ${'Windows'} | ${'mod+shift+p'} | ${['Ctrl', 'Shift', 'P']} + ${'MacIntel'} | ${'mod+shift+o'} | ${['⌘', '⇧', 'O']} + ${'Windows'} | ${'mod+shift+o'} | ${['Ctrl', 'Shift', 'O']} + ${'MacIntel'} | ${['mod', 'shift', 'r']} | ${['⌘', '⇧', 'R']} + ${'Windows'} | ${['mod', 'shift', 'r']} | ${['Ctrl', 'Shift', 'R']} + ${'MacIntel'} | ${['mod', 'shift', 'e']} | ${['⌘', '⇧', 'E']} + ${'Windows'} | ${['mod', 'shift', 'e']} | ${['Ctrl', 'Shift', 'E']} + ${'MacIntel'} | ${'left'} | ${['←']} + ${'Windows'} | ${'left'} | ${['←']} + ${'MacIntel'} | ${'right'} | ${['→']} + ${'Windows'} | ${'right'} | ${['→']} + `( + 'should return $result for $shortcut on $userAgent', + ({ userAgent, shortcut, result }) => { + Object.defineProperty(navigator, 'userAgent', { + value: userAgent, + configurable: true + }); + + expect(prettifyShortcut(shortcut)).toStrictEqual(result); + } + ); + }); }); From 79f982127ca2d6426e70cfa5c65bb46804a3c5e6 Mon Sep 17 00:00:00 2001 From: mwritter Date: Thu, 28 Jul 2022 14:13:14 -0400 Subject: [PATCH 18/21] Clean up --- .../components/command-bar/results/index.tsx | 51 ++++++++++--------- .../components/command-bar/search/index.tsx | 2 +- .../spectacle/src/components/deck/deck.tsx | 6 +-- .../spectacle/src/components/deck/index.tsx | 2 +- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/packages/spectacle/src/components/command-bar/results/index.tsx b/packages/spectacle/src/components/command-bar/results/index.tsx index 66fc99912..d2a6e3fc5 100644 --- a/packages/spectacle/src/components/command-bar/results/index.tsx +++ b/packages/spectacle/src/components/command-bar/results/index.tsx @@ -3,7 +3,8 @@ import { ActionImpl, KBarResults, useMatches } from 'kbar'; import { prettifyShortcut } from '../../../utils/platform-keys'; import { KeyboardShortcutTypes, - KEYBOARD_SHORTCUTS + KEYBOARD_SHORTCUTS, + SYSTEM_FONT } from '../../../utils/constants'; import { Text } from '../../typography'; @@ -21,47 +22,51 @@ function getShortcutKeys({ id, shortcut = [] }: ActionImpl): string[] { } const ResultCommand = styled.div>` - display: flex; - justify-content: space-between; - align-items: center - background-color: ${(p) => (p.active ? 'lightsteelblue' : 'transparent')}; - padding: 0.5rem 1rem; - cursor: pointer; - `; + display: flex; + justify-content: space-between; + align-items: center; + background-color: ${(p) => (p.active ? 'lightsteelblue' : 'transparent')}; + padding: 0.5rem 1rem; + cursor: pointer; + height: 30px; +`; -const ResultSectionHeader = styled.div` +const ResultSectionHeader = styled(Text)` background-color: white; - padding-left: 1rem; - border-bottom: 1px solid rgba(0 0 0 / 0.1); - font-size: smaller; - margin: 0 1rem; + color: gray; + margin: 0 2rem; + padding: 0.5rem 0; + font-size: small; + font-weight: bold; + font-family: ${SYSTEM_FONT}; `; const ResultShortcut = styled.span` display: flex; gap: 5px; - font-size: 1.3em; `; const ResultShortcutKey = styled.kbd` + display: flex; + justify-content: center; + align-items: center; background-color: #eee; - border-radius: 3px; + border-radius: 5px; border: 1px solid #b4b4b4; padding: 5px 10px; + min-width: 20px; + height: 25px; white-space: nowrap; + font-family: ${SYSTEM_FONT}; `; -function onRender({ item, active }: RenderParams): JSX.Element { +function onRender({ item, active }: RenderParams) { if (typeof item === 'string') { - return ( - - {item} - - ); + return {item}; } else { return ( - {item.name} + {item.name} {getShortcutKeys(item).map( (key) => @@ -77,7 +82,7 @@ function onRender({ item, active }: RenderParams): JSX.Element { } } -const CommandBarResults = (): JSX.Element => { +const CommandBarResults = () => { const { results } = useMatches(); return ; }; diff --git a/packages/spectacle/src/components/command-bar/search/index.tsx b/packages/spectacle/src/components/command-bar/search/index.tsx index 19151b908..0dccbda77 100644 --- a/packages/spectacle/src/components/command-bar/search/index.tsx +++ b/packages/spectacle/src/components/command-bar/search/index.tsx @@ -20,7 +20,7 @@ const KBarAnimatorStyled = styled(KBarAnimator)` box-shadow: rgb(0 0 0 / 50%) 0px 16px 70px; `; -const CommandBarSearch = (): JSX.Element => { +const CommandBarSearch = () => { return ( diff --git a/packages/spectacle/src/components/deck/deck.tsx b/packages/spectacle/src/components/deck/deck.tsx index 20e2282ff..7f7d7aee5 100644 --- a/packages/spectacle/src/components/deck/deck.tsx +++ b/packages/spectacle/src/components/deck/deck.tsx @@ -228,14 +228,14 @@ export const DeckInternal = forwardRef( name: 'Next Slide', keywords: 'next', perform: () => stepForward(), - section: 'Slides' + section: 'Slide' }, { id: KEYBOARD_SHORTCUTS_IDS.PREVIOUS_SLIDE, name: 'Previous Slide', keywords: 'previous', perform: () => stepBackward(), - section: 'Slides' + section: 'Slide' }, { id: 'Restart Presentation', @@ -246,7 +246,7 @@ export const DeckInternal = forwardRef( slideIndex: 0, stepIndex: 0 }), - section: 'Slides' + section: 'Slide' } ] : [] diff --git a/packages/spectacle/src/components/deck/index.tsx b/packages/spectacle/src/components/deck/index.tsx index 7e8988bc0..76559df0f 100644 --- a/packages/spectacle/src/components/deck/index.tsx +++ b/packages/spectacle/src/components/deck/index.tsx @@ -12,7 +12,7 @@ const View = ({ getCurrentMode, toggleMode, ...props -}: ModeActions & DeckProps): JSX.Element => { +}: ModeActions & DeckProps) => { const mode = getCurrentMode(); switch (mode) { case SPECTACLE_MODES.DEFAULT_MODE: From e82eb7504dfb8d5065eb09f88245a1a1b3374ed2 Mon Sep 17 00:00:00 2001 From: mwritter Date: Thu, 28 Jul 2022 15:58:03 -0400 Subject: [PATCH 19/21] Added some comments, and added back to default mode text --- .../command-bar/command-bar-actions.tsx | 47 ++++++++++++++++--- packages/spectacle/src/utils/constants.ts | 1 - 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx index d52b1c7bf..de0ccdc25 100644 --- a/packages/spectacle/src/components/command-bar/command-bar-actions.tsx +++ b/packages/spectacle/src/components/command-bar/command-bar-actions.tsx @@ -1,34 +1,67 @@ -import { KEYBOARD_SHORTCUTS_IDS, SPECTACLE_MODES } from '../../utils/constants'; +import { + KEYBOARD_SHORTCUTS_IDS, + SpectacleMode, + SPECTACLE_MODES +} from '../../utils/constants'; import useModes from '../../hooks/use-modes'; -const useCommandBarActions = () => { - const { toggleMode } = useModes(); +/** + * Kbar default actions, those that do not depend on dynamic logic, can be added here. + * To register actions dynamically use 'useRegisterActions' and make sure the action + * is registed within the KBarProvider. + * @see https://kbar.vercel.app/docs/concepts/actions + * Kbar action shortcuts dont seem to support all keybindings. If you need to utilize + * keybindings that are not supported you'll have to implement the keybinding seperately. + * @see useMousetrap + * To display keybindings that are not supported in the Kbar results, please use + * KEYBOARD_SHORTCUTS instead of Kbar actions 'shortcut' property. + * @see CommandBarResults getShortcutKeys + */ + +const spectacleModeDisplay = { + [SPECTACLE_MODES.DEFAULT_MODE]: 'Default Mode', + [SPECTACLE_MODES.PRESENTER_MODE]: 'Presenter Mode', + [SPECTACLE_MODES.OVERVIEW_MODE]: 'Overview Mode', + [SPECTACLE_MODES.PRINT_MODE]: 'Print Mode', + [SPECTACLE_MODES.EXPORT_MODE]: 'Export Mode' +}; + +const getName = (currentMode: string, mode: SpectacleMode) => { + const defaultMode = SPECTACLE_MODES.DEFAULT_MODE; + return currentMode === mode + ? `← Back to ${spectacleModeDisplay[defaultMode]}` + : spectacleModeDisplay[mode]; +}; + +const useCommandBarActions = () => { + const { toggleMode, getCurrentMode } = useModes(); + const currentMode = getCurrentMode(); return [ { id: KEYBOARD_SHORTCUTS_IDS.PRESENTER_MODE, - name: 'Presenter Mode', + name: getName(currentMode, SPECTACLE_MODES.PRESENTER_MODE), keywords: 'presenter', perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRESENTER_MODE }), section: 'Mode' }, { id: KEYBOARD_SHORTCUTS_IDS.OVERVIEW_MODE, - name: 'Overview Mode', + name: getName(currentMode, SPECTACLE_MODES.OVERVIEW_MODE), keywords: 'overview', perform: () => toggleMode({ newMode: SPECTACLE_MODES.OVERVIEW_MODE }), section: 'Mode' }, { id: KEYBOARD_SHORTCUTS_IDS.PRINT_MODE, - name: 'Print Mode', + name: getName(currentMode, SPECTACLE_MODES.PRINT_MODE), keywords: 'export', perform: () => toggleMode({ newMode: SPECTACLE_MODES.PRINT_MODE }), section: 'Mode' }, { id: KEYBOARD_SHORTCUTS_IDS.EXPORT_MODE, - name: 'Export Mode', + name: getName(currentMode, SPECTACLE_MODES.EXPORT_MODE), keywords: 'export', perform: () => toggleMode({ newMode: SPECTACLE_MODES.EXPORT_MODE }), section: 'Mode' diff --git a/packages/spectacle/src/utils/constants.ts b/packages/spectacle/src/utils/constants.ts index 8dc52768c..f5c05562d 100644 --- a/packages/spectacle/src/utils/constants.ts +++ b/packages/spectacle/src/utils/constants.ts @@ -3,7 +3,6 @@ export const DEFAULT_SLIDE_INDEX = 0; export const SYSTEM_FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Helvetica, sans-serif'; -// TODO: KBar could replace this but modifier keys don't seem to work with KBar export const KEYBOARD_SHORTCUTS = { PRESENTER_MODE: 'mod+shift+p', OVERVIEW_MODE: 'mod+shift+o', From ffa1eeb388fd34a02c3ad57a73061697bb4151c2 Mon Sep 17 00:00:00 2001 From: mwritter Date: Thu, 28 Jul 2022 16:17:23 -0400 Subject: [PATCH 20/21] added changeset --- .changeset/long-suns-smoke.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/long-suns-smoke.md diff --git a/.changeset/long-suns-smoke.md b/.changeset/long-suns-smoke.md new file mode 100644 index 000000000..0ff85c16c --- /dev/null +++ b/.changeset/long-suns-smoke.md @@ -0,0 +1,5 @@ +--- +'spectacle': minor +--- + +Utilize `Kbar` to allow users to quickly search and use the current commands Spectacle supports within presentations. From 57c5dfb6e34b4032cf4d620e894c4ddffb359701 Mon Sep 17 00:00:00 2001 From: Matthew Ritter Date: Thu, 28 Jul 2022 16:22:55 -0400 Subject: [PATCH 21/21] Update .changeset/long-suns-smoke.md Co-authored-by: Ryan Roemer --- .changeset/long-suns-smoke.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/long-suns-smoke.md b/.changeset/long-suns-smoke.md index 0ff85c16c..e3d3c30fb 100644 --- a/.changeset/long-suns-smoke.md +++ b/.changeset/long-suns-smoke.md @@ -2,4 +2,4 @@ 'spectacle': minor --- -Utilize `Kbar` to allow users to quickly search and use the current commands Spectacle supports within presentations. +Utilize `Kbar` to allow users to quickly search and use the current commands Spectacle supports within presentations. Fixes #1115