-
-
Notifications
You must be signed in to change notification settings - Fork 170
Add --bespoke.thumbnails option for slide thumbnails in presenter view #705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c55f6c0
85a9746
6a9077c
bad87be
6bb5232
88d11f5
ad2ecb5
ce43be1
56bf956
816e340
74ab265
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { | ||
| getViewMode, | ||
| ViewModeNormal, | ||
| ViewModeOverview, | ||
| ViewModePresenter, | ||
| } from '../utils' | ||
| import normalView from './normal-view' | ||
| import overviewView from './overview-view' | ||
|
|
||
| const bespokeOverview = () => { | ||
| const mode = getViewMode() | ||
|
|
||
| return { | ||
| [ViewModeNormal]: normalView, | ||
| [ViewModePresenter]: normalView, | ||
| [ViewModeOverview]: overviewView(), | ||
| }[mode] | ||
| } | ||
|
|
||
| export default bespokeOverview |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import { classPrefix, generateURLfromParams, storage } from '../utils' | ||
|
|
||
| export const overviewPrefix = `${classPrefix}overview-` as const | ||
|
|
||
| interface BespokeForOverview { | ||
| syncKey: string | ||
| [key: string]: any | ||
| } | ||
|
|
||
| const validateDeck = (deck: any): deck is BespokeForOverview => | ||
| deck.syncKey && typeof deck.syncKey === 'string' | ||
|
|
||
| const normalView = (deck) => { | ||
| if (!validateDeck(deck)) return | ||
|
|
||
| Object.defineProperties(deck, { | ||
| openOverviewView: { enumerable: true, value: openOverviewView }, | ||
| overviewUrl: { enumerable: true, get: overviewUrl }, | ||
| }) | ||
|
|
||
| if (storage.available) | ||
| document.addEventListener('keydown', (e) => { | ||
| if (e.key === 'o' && !e.altKey && !e.ctrlKey && !e.metaKey) { | ||
| e.preventDefault() | ||
| deck.openOverviewView() | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| function openOverviewView(this: BespokeForOverview) { | ||
| const { max, floor } = Math | ||
| const w = max(floor(window.innerWidth * 0.85), 640) | ||
| const h = max(floor(window.innerHeight * 0.85), 360) | ||
|
|
||
| return window.open( | ||
| this.overviewUrl, | ||
| overviewPrefix + this.syncKey, | ||
| `width=${w},height=${h},menubar=no,toolbar=no` | ||
| ) | ||
| } | ||
|
|
||
| function overviewUrl(this: BespokeForOverview) { | ||
| const params = new URLSearchParams(location.search) | ||
|
|
||
| params.set('view', 'overview') | ||
| params.set('sync', this.syncKey) | ||
|
|
||
| return generateURLfromParams(params) | ||
| } | ||
|
|
||
| export default normalView | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import { classPrefix } from '../utils' | ||
|
|
||
| const overviewActiveClass = `${classPrefix}overview-active` | ||
|
|
||
| const overviewView = () => (deck) => { | ||
| const { title } = document | ||
| document.title = `[Overview]${title ? ` - ${title}` : ''}` | ||
|
|
||
| const slides = (): SVGElement[] => deck.slides | ||
| const slideCount = () => slides().length | ||
| const columns = () => Math.ceil(Math.sqrt(slideCount())) | ||
|
|
||
| const applyLayout = () => { | ||
| const cols = columns() | ||
| const rows = Math.ceil(slideCount() / cols) | ||
| const parent = deck.parent as HTMLElement | ||
| const rect = parent.getBoundingClientRect() | ||
| const gap = 20 | ||
| const cellW = (rect.width - gap * (cols + 1)) / cols | ||
| const cellH = (rect.height - gap * (rows + 1)) / rows | ||
|
|
||
| const scaleX = cellW / rect.width | ||
| const scaleY = cellH / rect.height | ||
| const cellScale = Math.min(scaleX, scaleY) | ||
|
|
||
| const renderedW = rect.width * cellScale | ||
| const renderedH = rect.height * cellScale | ||
|
|
||
| slides().forEach((slide, i) => { | ||
| const col = i % cols | ||
| const row = Math.floor(i / cols) | ||
| const x = gap + col * (renderedW + gap) | ||
| const y = gap + row * (renderedH + gap) | ||
|
|
||
| slide.style.transformOrigin = '0 0' | ||
| slide.style.transform = `translate(${x}px, ${y}px) scale(${cellScale})` | ||
| }) | ||
| } | ||
|
Comment on lines
+13
to
+38
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use CSS Grid to make simpler JS. |
||
|
|
||
| const updateHighlight = (index: number) => { | ||
| slides().forEach((slide, i) => { | ||
| slide.classList.toggle(overviewActiveClass, i === index) | ||
| }) | ||
| } | ||
|
|
||
| // Click a slide to navigate (syncs to other windows via bespokeSync) | ||
| slides().forEach((slide, i) => { | ||
| slide.addEventListener('click', () => { | ||
| deck.slide(i) | ||
| }) | ||
|
Comment on lines
+48
to
+50
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a11y: Each slide should be focusable by setting |
||
| }) | ||
|
|
||
| // Arrow key navigation within the grid | ||
| document.addEventListener('keydown', (e) => { | ||
| const cols = columns() | ||
| const current = deck.slide() | ||
|
|
||
| if (e.key === 'ArrowRight') { | ||
| e.preventDefault() | ||
| deck.slide(Math.min(current + 1, slideCount() - 1)) | ||
| } else if (e.key === 'ArrowLeft') { | ||
| e.preventDefault() | ||
| deck.slide(Math.max(current - 1, 0)) | ||
| } else if (e.key === 'ArrowDown') { | ||
| e.preventDefault() | ||
| deck.slide(Math.min(current + cols, slideCount() - 1)) | ||
| } else if (e.key === 'ArrowUp') { | ||
| e.preventDefault() | ||
| deck.slide(Math.max(current - cols, 0)) | ||
| } | ||
| }) | ||
|
|
||
| // Update highlight on any slide change (including sync from other windows) | ||
| deck.on('fragment', ({ index }) => { | ||
| updateHighlight(index) | ||
| }) | ||
|
|
||
| // Initial layout and highlight | ||
| applyLayout() | ||
| updateHighlight(deck.slide()) | ||
|
|
||
| // Recalculate layout on resize | ||
| window.addEventListener('resize', () => applyLayout()) | ||
| } | ||
|
|
||
| export default overviewView | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to be overlay on the current view instead of opening new window.