@textcortex/slidewise 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@textcortex/slidewise",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Embeddable React PPTX editor.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -62,6 +62,26 @@
62
62
  --slidewise-bg-pill: var(--smart-grad);
63
63
  --slidewise-bg-unsaved-badge: rgba(232, 80, 76, 0.12);
64
64
 
65
+ /* Motion tokens. Hosts retune the editor's whole animation feel by
66
+ overriding these. Defaults match Material 3 "standard" easings. */
67
+ --slidewise-duration-instant: 0ms;
68
+ --slidewise-duration-fast: 120ms;
69
+ --slidewise-duration-base: 200ms;
70
+ --slidewise-duration-slow: 320ms;
71
+ --slidewise-easing-standard: cubic-bezier(0.2, 0, 0, 1);
72
+ --slidewise-easing-emphasized: cubic-bezier(0.05, 0.7, 0.1, 1);
73
+ --slidewise-easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
74
+
75
+ /* Per-region animation flags. Set to 0 on a wrapping scope to disable a
76
+ specific region's animations without killing motion globally. They are
77
+ read internally by region-scoped transitions; CSS rules use them via
78
+ `transition-duration: calc(var(--slidewise-duration-base) * var(--slidewise-anim-topbar))`. */
79
+ --slidewise-anim-topbar: 1;
80
+ --slidewise-anim-rail: 1;
81
+ --slidewise-anim-canvas: 1;
82
+ --slidewise-anim-floating-toolbar: 1;
83
+ --slidewise-anim-play-mode: 1;
84
+
65
85
  /* Layout backgrounds */
66
86
  --app-bg: #ffffff;
67
87
  --rail-bg: #fafafb;
@@ -295,3 +315,40 @@
295
315
  transition: none;
296
316
  }
297
317
  }
318
+
319
+ /*
320
+ * Reduced motion plumbing.
321
+ *
322
+ * <Slidewise.Root reduceMotion="system"> → no class; respects the
323
+ * OS preference via the media
324
+ * query below.
325
+ * <Slidewise.Root reduceMotion={true}> → adds `.reduce-motion` class
326
+ * for hard-off.
327
+ * <Slidewise.Root reduceMotion={false}> → adds `.reduce-motion-off`
328
+ * class to override the OS
329
+ * preference (testing /
330
+ * previewing animations).
331
+ */
332
+ .slidewise-editor.reduce-motion,
333
+ .slidewise-editor.reduce-motion *,
334
+ .slidewise-editor.reduce-motion *::before,
335
+ .slidewise-editor.reduce-motion *::after {
336
+ animation-duration: 0ms !important;
337
+ animation-delay: 0ms !important;
338
+ transition-duration: 0ms !important;
339
+ transition-delay: 0ms !important;
340
+ scroll-behavior: auto !important;
341
+ }
342
+
343
+ @media (prefers-reduced-motion: reduce) {
344
+ .slidewise-editor:not(.reduce-motion-off),
345
+ .slidewise-editor:not(.reduce-motion-off) *,
346
+ .slidewise-editor:not(.reduce-motion-off) *::before,
347
+ .slidewise-editor:not(.reduce-motion-off) *::after {
348
+ animation-duration: 0ms !important;
349
+ animation-delay: 0ms !important;
350
+ transition-duration: 0ms !important;
351
+ transition-delay: 0ms !important;
352
+ scroll-behavior: auto !important;
353
+ }
354
+ }
@@ -17,6 +17,7 @@ import {
17
17
  import type { SlidewiseIcons } from "./compound/IconContext";
18
18
  import type { SlidewiseLabels } from "./compound/LabelsContext";
19
19
  import type { SlidewiseSurfaces } from "./compound/SurfacesContext";
20
+ import type { Transition } from "framer-motion";
20
21
  import type { Deck } from "@/lib/types";
21
22
  import "./SlidewiseEditor.css";
22
23
 
@@ -67,6 +68,16 @@ export interface SlidewiseEditorProps {
67
68
  showBottomToolbar?: boolean;
68
69
  /** Override the bundled Geist font; sets `--font-geist-sans` on the root. */
69
70
  fontFamily?: string;
71
+ /**
72
+ * Reduced-motion behavior. `"system"` (default) respects the OS
73
+ * preference; `true` forces motion off; `false` forces it on.
74
+ */
75
+ reduceMotion?: boolean | "system";
76
+ /**
77
+ * Default framer-motion transition applied via `<MotionConfig>`. Use
78
+ * this to retune the editor's animation feel globally.
79
+ */
80
+ transition?: Transition;
70
81
  /**
71
82
  * Per-action icon overrides. Pass a ReactNode for any of `undo`, `redo`,
72
83
  * `save`, `play`, `themeLight`, `themeDark`, `export`, `smart` to skin the
@@ -135,6 +146,8 @@ export const SlidewiseEditor = forwardRef<
135
146
  showTopBar = true,
136
147
  showBottomToolbar = true,
137
148
  fontFamily,
149
+ reduceMotion,
150
+ transition,
138
151
  icons,
139
152
  labels,
140
153
  surfaces,
@@ -160,6 +173,8 @@ export const SlidewiseEditor = forwardRef<
160
173
  theme,
161
174
  initialSlideId,
162
175
  fontFamily,
176
+ reduceMotion,
177
+ transition,
163
178
  icons,
164
179
  labels,
165
180
  surfaces,
@@ -13,6 +13,7 @@ import type { SlidewiseIcons } from "./compound/IconContext";
13
13
  import type { SlidewiseLabels } from "./compound/LabelsContext";
14
14
  import type { SlidewiseSurfaces } from "./compound/SurfacesContext";
15
15
  import { DEFAULT_LABELS } from "./compound/LabelsContext";
16
+ import type { Transition } from "framer-motion";
16
17
  import type { HistoryState, SelectionSnapshot } from "./compound/SlidewiseRoot";
17
18
 
18
19
  export interface SlidewiseFileEditorProps {
@@ -105,6 +106,13 @@ export interface SlidewiseFileEditorProps {
105
106
  * `--slidewise-bg-*` CSS variables.
106
107
  */
107
108
  surfaces?: SlidewiseSurfaces;
109
+ /**
110
+ * Reduced-motion behavior. `"system"` (default) respects the OS
111
+ * preference; `true` forces motion off; `false` forces it on.
112
+ */
113
+ reduceMotion?: boolean | "system";
114
+ /** Default framer-motion transition applied via `<MotionConfig>`. */
115
+ transition?: Transition;
108
116
  className?: string;
109
117
  style?: CSSProperties;
110
118
  /**
@@ -209,6 +217,8 @@ export const SlidewiseFileEditor = forwardRef<
209
217
  icons,
210
218
  labels,
211
219
  surfaces,
220
+ reduceMotion,
221
+ transition,
212
222
  className,
213
223
  style,
214
224
  parse = parsePptx,
@@ -384,6 +394,8 @@ export const SlidewiseFileEditor = forwardRef<
384
394
  icons={icons}
385
395
  labels={labels}
386
396
  surfaces={surfaces}
397
+ reduceMotion={reduceMotion}
398
+ transition={transition}
387
399
  onChange={(next) => {
388
400
  onChangeRef.current?.(next);
389
401
  }}
@@ -18,6 +18,7 @@ import { collectFontFamilies, ensureGoogleFontsLoaded } from "@/lib/fonts";
18
18
  import type { Deck } from "@/lib/types";
19
19
  import { GridView } from "@/components/editor/GridView";
20
20
  import { PlayMode } from "@/components/editor/PlayMode";
21
+ import { MotionConfig, type Transition } from "framer-motion";
21
22
  import { HostProvider } from "./HostContext";
22
23
  import { IconProvider, type SlidewiseIcons } from "./IconContext";
23
24
  import { ReadOnlyProvider } from "./ReadOnlyContext";
@@ -72,6 +73,29 @@ export interface SlidewiseRootProps {
72
73
  initialSlideId?: string;
73
74
  /** Override the default Geist font; sets `--slidewise-font-sans`. */
74
75
  fontFamily?: string;
76
+ /**
77
+ * Controls reduced-motion behavior.
78
+ *
79
+ * - `"system"` (default) — respect the user's OS preference via the
80
+ * `prefers-reduced-motion` media query.
81
+ * - `true` — force all CSS animations + transitions off and tell
82
+ * framer-motion to skip motion. Use for hosts whose own product
83
+ * already has a global motion-off toggle.
84
+ * - `false` — force motion on even when the OS reports reduced-motion.
85
+ * Useful for previewing animations during development; not generally
86
+ * recommended in production since it overrides an accessibility hint.
87
+ */
88
+ reduceMotion?: boolean | "system";
89
+ /**
90
+ * Default framer-motion transition. Passed through to a wrapping
91
+ * `<MotionConfig>` so every motion component inside the editor inherits
92
+ * it. Useful for retuning the editor's overall feel (faster, springier,
93
+ * etc.) without touching individual components.
94
+ *
95
+ * For CSS transitions, override the duration/easing tokens instead —
96
+ * `--slidewise-duration-base`, `--slidewise-easing-standard`, etc.
97
+ */
98
+ transition?: Transition;
75
99
  /**
76
100
  * Per-action icon overrides for the chrome. Hosts pass any subset to
77
101
  * skin Slidewise with their own icon set; missing slots fall back to
@@ -212,6 +236,8 @@ function RootInner({
212
236
  theme,
213
237
  initialSlideId,
214
238
  fontFamily,
239
+ reduceMotion = "system",
240
+ transition,
215
241
  icons,
216
242
  labels,
217
243
  surfaces,
@@ -461,26 +487,50 @@ function RootInner({
461
487
  ? { ...style, ...(surfaceVars as CSSProperties) }
462
488
  : style;
463
489
 
490
+ // Map our reduceMotion enum to:
491
+ // - a CSS class on the root (drives the @media + class rules in
492
+ // SlidewiseEditor.css)
493
+ // - framer-motion's reducedMotion prop on MotionConfig
494
+ const motionClass =
495
+ reduceMotion === true
496
+ ? "reduce-motion"
497
+ : reduceMotion === false
498
+ ? "reduce-motion-off"
499
+ : "";
500
+ const fmReducedMotion =
501
+ reduceMotion === true
502
+ ? "always"
503
+ : reduceMotion === false
504
+ ? "never"
505
+ : "user";
506
+ const combinedClassName = motionClass
507
+ ? className
508
+ ? `${className} ${motionClass}`
509
+ : motionClass
510
+ : className;
511
+
464
512
  return (
465
- <ReadOnlyProvider readOnly={readOnly}>
466
- <IconProvider icons={icons ?? {}}>
467
- <LabelsProvider labels={labels}>
468
- <SurfacesProvider surfaces={surfaces}>
469
- <DirtyProvider dirty={dirty}>
470
- <HostProvider callbacks={{ onSave: wrappedSave, onExport }}>
471
- <RootShell
472
- fontFamily={fontFamily}
473
- className={className}
474
- style={mergedStyle}
475
- >
476
- {children}
477
- </RootShell>
478
- </HostProvider>
479
- </DirtyProvider>
480
- </SurfacesProvider>
481
- </LabelsProvider>
482
- </IconProvider>
483
- </ReadOnlyProvider>
513
+ <MotionConfig reducedMotion={fmReducedMotion} transition={transition}>
514
+ <ReadOnlyProvider readOnly={readOnly}>
515
+ <IconProvider icons={icons ?? {}}>
516
+ <LabelsProvider labels={labels}>
517
+ <SurfacesProvider surfaces={surfaces}>
518
+ <DirtyProvider dirty={dirty}>
519
+ <HostProvider callbacks={{ onSave: wrappedSave, onExport }}>
520
+ <RootShell
521
+ fontFamily={fontFamily}
522
+ className={combinedClassName}
523
+ style={mergedStyle}
524
+ >
525
+ {children}
526
+ </RootShell>
527
+ </HostProvider>
528
+ </DirtyProvider>
529
+ </SurfacesProvider>
530
+ </LabelsProvider>
531
+ </IconProvider>
532
+ </ReadOnlyProvider>
533
+ </MotionConfig>
484
534
  );
485
535
  }
486
536